深入理解SpringBoot之启动探究

本文内容纲要:

- 一、基于Springframework的事件机制

- 1.1、JDK中的事件接口与类

- 1.2、spring中的事件类

- 1.3、基于Spring的自定义事件

- 二、SpringApplication启动分析

-  2.1、SpringApplication初始化分析

- 2.2、SpringApplication的run方法分析

- 1. 获取SpringApplicationRunListener

- 2、调用所有的SpringApplicationRunListenner的start方法

- 3、执行prepareEnvironment方法

- 4、根据当前的Environment打印Banner

- 5、创建ConfigurableApplicationContext对象与FailureAnalyzers

- 6、调用prepareContext方法

- 7、调用 refreshContext方法

- 8、执行afterRefresh()

- 9、调用 listeners.finished方法

- 10、启动时的异常处理

- 三、测试示例

-   1、创建MyBootStrapApplicationListener示例:

-   2、创建MyCommandRunner

-   3、在spring.factories配置ApplicationListener

- 四、SpringBoot启动总结

  SpringApplication是SpringBoot的启动程序,我们通过它的run方法可以快速启动一个SpringBoot应用。可是这里面到底发生了什么?它是处于什么样的机制简化我们程序启动的?接下来我们就带着这两个问题来揭开SpringBoot启动过程的神秘面纱。

一、基于Springframework的事件机制

  事件是SpringBoot的启动核心之一。对于事件我想大家都不陌生,在javaAWT中事件是在常见不过的了。

1.1、JDK中的事件接口与类

  首先我们看一下EventObject,这个类定义了一个事件,该类中的source属性可以用来表示事件源(哪个对象触发的事件)

ImageImage

/*

* Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved.

* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*/

package java.util;

/**

* <p>

* The root class from which all event state objects shall be derived.

* <p>

* All Events are constructed with a reference to the object, the "source",

* that is logically deemed to be the object upon which the Event in question

* initially occurred upon.

*

* @since JDK1.1

*/

public class EventObject implements java.io.Serializable {

private static final long serialVersionUID = 5516075349620653480L;

/**

* The object on which the Event initially occurred.

*/

protected transient Object source;

/**

* Constructs a prototypical Event.

*

* @param source The object on which the Event initially occurred.

* @exception IllegalArgumentException if source is null.

*/

public EventObject(Object source) {

if (source == null)

throw new IllegalArgumentException("null source");

this.source = source;

}

/**

* The object on which the Event initially occurred.

*

* @return The object on which the Event initially occurred.

*/

public Object getSource() {

return source;

}

/**

* Returns a String representation of this EventObject.

*

* @return A a String representation of this EventObject.

*/

public String toString() {

return getClass().getName() + "[source=" + source + "]";

}

}

View Code

  我们看一下在AWT中很经典的MouseEvent的类关系图:

Image

  其次我们需要了解一下关于事件监听的接口EventListener:

package java.util;

/**

* A tagging interface that all event listener interfaces must extend.

* @since JDK1.1

*/

public interface EventListener {

}

  这个接口很简单,没有任何方法,但是JDK文档已经明确告诉我们:所有事件的监听必须继承此接口,那么我在贴出来一个MouseListener接口示例:

ImageImage

/*

* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.

* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*

*/

package java.awt.event;

import java.util.EventListener;

/**

* The listener interface for receiving "interesting" mouse events

* (press, release, click, enter, and exit) on a component.

* (To track mouse moves and mouse drags, use the

* <code>MouseMotionListener</code>.)

* <P>

* The class that is interested in processing a mouse event

* either implements this interface (and all the methods it

* contains) or extends the abstract <code>MouseAdapter</code> class

* (overriding only the methods of interest).

* <P>

* The listener object created from that class is then registered with a

* component using the component's <code>addMouseListener</code>

* method. A mouse event is generated when the mouse is pressed, released

* clicked (pressed and released). A mouse event is also generated when

* the mouse cursor enters or leaves a component. When a mouse event

* occurs, the relevant method in the listener object is invoked, and

* the <code>MouseEvent</code> is passed to it.

*

* @author Carl Quinn

*

* @see MouseAdapter

* @see MouseEvent

* @see <a href="https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html">Tutorial: Writing a Mouse Listener</a>

*

* @since 1.1

*/

public interface MouseListener extends EventListener {

/**

* Invoked when the mouse button has been clicked (pressed

* and released) on a component.

*/

public void mouseClicked(MouseEvent e);

/**

* Invoked when a mouse button has been pressed on a component.

*/

public void mousePressed(MouseEvent e);

/**

* Invoked when a mouse button has been released on a component.

*/

public void mouseReleased(MouseEvent e);

/**

* Invoked when the mouse enters a component.

*/

public void mouseEntered(MouseEvent e);

/**

* Invoked when the mouse exits a component.

*/

public void mouseExited(MouseEvent e);

}

View Code

  我们可以看到MouseListener继承了EventListener接口,接口中的方法参数都为MouseEvent。

1.2、spring中的事件类

  Spring中也给我们提供了一套事件处理机制,其中几个较为关键的接口和类分别是:

    ApplicationEvent

    ApplicationListener

    ApplicationEventPublisher

    ApplicationEventMulticaster

  下面我们来依次看一下这几个类与接口:

 ** ApplicationEvent:**

ImageImage

/*

* Copyright 2002-2015 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.context;

import java.util.EventObject;

/**

* Class to be extended by all application events. Abstract as it

* doesn't make sense for generic events to be published directly.

*

* @author Rod Johnson

* @author Juergen Hoeller

*/

public abstract class ApplicationEvent extends EventObject {

/** use serialVersionUID from Spring 1.2 for interoperability */

private static final long serialVersionUID = 7099057708183571937L;

/** System time when the event happened */

private final long timestamp;

/**

* Create a new ApplicationEvent.

* @param source the object on which the event initially occurred (never {@code null})

*/

public ApplicationEvent(Object source) {

super(source);

this.timestamp = System.currentTimeMillis();

}

/**

* Return the system time in milliseconds when the event happened.

*/

public final long getTimestamp() {

return this.timestamp;

}

}

View Code

  在这里我们可以明确看到该类直接继承EventObject

 ** ApplicationListener:**

ImageImage

/*

* Copyright 2002-2015 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.context;

import java.util.EventListener;

/**

* Interface to be implemented by application event listeners.

* Based on the standard {@code java.util.EventListener} interface

* for the Observer design pattern.

*

* <p>As of Spring 3.0, an ApplicationListener can generically declare the event type

* that it is interested in. When registered with a Spring ApplicationContext, events

* will be filtered accordingly, with the listener getting invoked for matching event

* objects only.

*

* @author Rod Johnson

* @author Juergen Hoeller

* @param <E> the specific ApplicationEvent subclass to listen to

* @see org.springframework.context.event.ApplicationEventMulticaster

*/

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

/**

* Handle an application event.

* @param event the event to respond to

*/

void onApplicationEvent(E event);

}

View Code

  我们可以看到该接口继承EventListener

  ApplicationEventPublisher:

ImageImage

/*

* Copyright 2002-2015 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.context;

/**

* Interface that encapsulates event publication functionality.

* Serves as super-interface for {@link ApplicationContext}.

*

* @author Juergen Hoeller

* @author Stephane Nicoll

* @since 1.1.1

* @see ApplicationContext

* @see ApplicationEventPublisherAware

* @see org.springframework.context.ApplicationEvent

* @see org.springframework.context.event.EventPublicationInterceptor

*/

public interface ApplicationEventPublisher {

/**

* Notify all <span>matching</span> listeners registered with this

* application of an application event. Events may be framework events

* (such as RequestHandledEvent) or application-specific events.

* @param event the event to publish

* @see org.springframework.web.context.support.RequestHandledEvent

*/

void publishEvent(ApplicationEvent event);

/**

* Notify all <span>matching</span> listeners registered with this

* application of an event.

* <p>If the specified {@code event} is not an {@link ApplicationEvent},

* it is wrapped in a {@link PayloadApplicationEvent}.

* @param event the event to publish

* @since 4.2

* @see PayloadApplicationEvent

*/

void publishEvent(Object event);

}

View Code

  这个接口比较重要,它使用来触发一个事件的(虽然方法的名称为发布事件),调用方法publishEvent过后,事件对应的listener将会执行相应的内容

 ** ApplicationEventMulticaster**

**  **该接口管理ApplicationListener的同时可以执行listener监听事件的方法:

ImageImage

/*

* Copyright 2002-2017 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.context.event;

import org.springframework.context.ApplicationEvent;

import org.springframework.context.ApplicationListener;

import org.springframework.core.ResolvableType;

/**

* Interface to be implemented by objects that can manage a number of

* {@link ApplicationListener} objects, and publish events to them.

*

* <p>An {@link org.springframework.context.ApplicationEventPublisher}, typically

* a Spring {@link org.springframework.context.ApplicationContext}, can use an

* ApplicationEventMulticaster as a delegate for actually publishing events.

*

* @author Rod Johnson

* @author Juergen Hoeller

* @author Stephane Nicoll

*/

public interface ApplicationEventMulticaster {

/**

* Add a listener to be notified of all events.

* @param listener the listener to add

*/

void addApplicationListener(ApplicationListener<?> listener);

/**

* Add a listener bean to be notified of all events.

* @param listenerBeanName the name of the listener bean to add

*/

void addApplicationListenerBean(String listenerBeanName);

/**

* Remove a listener from the notification list.

* @param listener the listener to remove

*/

void removeApplicationListener(ApplicationListener<?> listener);

/**

* Remove a listener bean from the notification list.

* @param listenerBeanName the name of the listener bean to add

*/

void removeApplicationListenerBean(String listenerBeanName);

/**

* Remove all listeners registered with this multicaster.

* <p>After a remove call, the multicaster will perform no action

* on event notification until new listeners are being registered.

*/

void removeAllListeners();

/**

* Multicast the given application event to appropriate listeners.

* <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}

* if possible as it provides a better support for generics-based events.

* @param event the event to multicast

*/

void multicastEvent(ApplicationEvent event);

/**

* Multicast the given application event to appropriate listeners.

* <p>If the {@code eventType} is {@code null}, a default type is built

* based on the {@code event} instance.

* @param event the event to multicast

* @param eventType the type of event (can be null)

* @since 4.2

*/

void multicastEvent(ApplicationEvent event, ResolvableType eventType);

}

View Code

**  我们可以看一下其子类SimpleApplicationEventMulticaster 的源码:**

ImageImage

/*

* Copyright 2002-2017 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.context.event;

import java.util.concurrent.Executor;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.BeanFactory;

import org.springframework.context.ApplicationEvent;

import org.springframework.context.ApplicationListener;

import org.springframework.core.ResolvableType;

import org.springframework.util.ErrorHandler;

/**

* Simple implementation of the {@link ApplicationEventMulticaster} interface.

*

* <p>Multicasts all events to all registered listeners, leaving it up to

* the listeners to ignore events that they are not interested in.

* Listeners will usually perform corresponding {@code instanceof}

* checks on the passed-in event object.

*

* <p>By default, all listeners are invoked in the calling thread.

* This allows the danger of a rogue listener blocking the entire application,

* but adds minimal overhead. Specify an alternative task executor to have

* listeners executed in different threads, for example from a thread pool.

*

* @author Rod Johnson

* @author Juergen Hoeller

* @author Stephane Nicoll

* @see #setTaskExecutor

*/

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

private Executor taskExecutor;

private ErrorHandler errorHandler;

/**

* Create a new SimpleApplicationEventMulticaster.

*/

public SimpleApplicationEventMulticaster() {

}

/**

* Create a new SimpleApplicationEventMulticaster for the given BeanFactory.

*/

public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {

setBeanFactory(beanFactory);

}

/**

* Set a custom executor (typically a {@link org.springframework.core.task.TaskExecutor})

* to invoke each listener with.

* <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},

* executing all listeners synchronously in the calling thread.

* <p>Consider specifying an asynchronous task executor here to not block the

* caller until all listeners have been executed. However, note that asynchronous

* execution will not participate in the caller's thread context (class loader,

* transaction association) unless the TaskExecutor explicitly supports this.

* @see org.springframework.core.task.SyncTaskExecutor

* @see org.springframework.core.task.SimpleAsyncTaskExecutor

*/

public void setTaskExecutor(Executor taskExecutor) {

this.taskExecutor = taskExecutor;

}

/**

* Return the current task executor for this multicaster.

*/

protected Executor getTaskExecutor() {

return this.taskExecutor;

}

/**

* Set the {@link ErrorHandler} to invoke in case an exception is thrown

* from a listener.

* <p>Default is none, with a listener exception stopping the current

* multicast and getting propagated to the publisher of the current event.

* If a {@linkplain #setTaskExecutor task executor} is specified, each

* individual listener exception will get propagated to the executor but

* won't necessarily stop execution of other listeners.

* <p>Consider setting an {@link ErrorHandler} implementation that catches

* and logs exceptions (a la

* {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_SUPPRESS_ERROR_HANDLER})

* or an implementation that logs exceptions while nevertheless propagating them

* (e.g. {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_PROPAGATE_ERROR_HANDLER}).

* @since 4.1

*/

public void setErrorHandler(ErrorHandler errorHandler) {

this.errorHandler = errorHandler;

}

/**

* Return the current error handler for this multicaster.

* @since 4.1

*/

protected ErrorHandler getErrorHandler() {

return this.errorHandler;

}

@Override

public void multicastEvent(ApplicationEvent event) {

multicastEvent(event, resolveDefaultEventType(event));

}

@Override

public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {

ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));

for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {

Executor executor = getTaskExecutor();

if (executor != null) {

executor.execute(new Runnable() {

@Override

public void run() {

invokeListener(listener, event);

}

});

}

else {

invokeListener(listener, event);

}

}

}

private ResolvableType resolveDefaultEventType(ApplicationEvent event) {

return ResolvableType.forInstance(event);

}

/**

* Invoke the given listener with the given event.

* @param listener the ApplicationListener to invoke

* @param event the current event to propagate

* @since 4.1

*/

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {

ErrorHandler errorHandler = getErrorHandler();

if (errorHandler != null) {

try {

doInvokeListener(listener, event);

}

catch (Throwable err) {

errorHandler.handleError(err);

}

}

else {

doInvokeListener(listener, event);

}

}

@SuppressWarnings({"unchecked", "rawtypes"})

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {

try {

listener.onApplicationEvent(event);

}

catch (ClassCastException ex) {

String msg = ex.getMessage();

if (msg == null || msg.startsWith(event.getClass().getName())) {

// Possibly a lambda-defined listener which we could not resolve the generic event type for

Log logger = LogFactory.getLog(getClass());

if (logger.isDebugEnabled()) {

logger.debug("Non-matching event type for listener: " + listener, ex);

}

}

else {

throw ex;

}

}

}

}

View Code

  请大家看一下 doInvokeListener方法,该方法用于执行事件的监听方法

1.3、基于Spring的自定义事件

  在这里我们模拟一个场景,当感到饥饿时,通知厨师做饭

  定义事件:

ImageImage

package org.hzgj.spring.study.event;

import org.springframework.context.ApplicationEvent;

/**

* 定义一个描饥饿状态的事件

*

* @author chen.nie

* @date 2018/4/26

**/

public class HungryEvent extends ApplicationEvent {

/**

* Create a new ApplicationEvent.

*

* @param source the object on which the event initially occurred (never {@code null})

*/

public HungryEvent(Object source) {

super(source);

}

}

View Code

  定义Person:

ImageImage

package org.hzgj.spring.study.event;

import org.springframework.context.ApplicationEventPublisher;

import org.springframework.context.ApplicationEventPublisherAware;

import org.springframework.stereotype.Component;

/**

* Person类,如果属性hungry的值为0,则通知厨师做饭吃。

*/

@Component

public class Person implements ApplicationEventPublisherAware {

private int hungry;

private String name;

public int getHungry() {

return hungry;

}

public void setHungry(int hungry) {

this.hungry = hungry;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

private ApplicationEventPublisher applicationEventPublisher;

@Override

public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {

this.applicationEventPublisher = applicationEventPublisher;

}

public void isNeedEat() {

if (this.hungry == 0) {

System.out.println("太饿了,需要吃东西");

new Thread(() -> this.applicationEventPublisher.publishEvent(new HungryEvent(this))).start();

System.out.println("通知完毕");

}

}

}

View Code

  注意这里面利用spring的aware模式拿到ApplicationEventPublisher对象,在Spring里有若干个Aware,比如说ApplicationContextAware BeanFactoryAware等。

  定义厨师类:

ImageImage

package org.hzgj.spring.study.event;

import org.springframework.context.ApplicationListener;

import org.springframework.stereotype.Component;

/**

* 厨师类用于对饥饿事件的监听...

*

* @author chen.nie

* @date 2018/4/26

**/

@Component

public class Chef implements ApplicationListener<HungryEvent> {

@Override

public void onApplicationEvent(HungryEvent event) {

if (event.getSource() instanceof Person) {

Person person = (Person) event.getSource();

System.out.println(person.getName() + "饿了,开始做饭");

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("做饭完毕....开始吃吧");

}

}

}

View Code

 spring-config.xml:

ImageImage

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="org.hzgj"/>

</beans>

View Code

 ** Main方法:**

ImageImage

package org.hzgj.spring.study;

import org.hzgj.spring.study.event.Person;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.naming.NamingException;

import java.io.IOException;

public class Main {

public static void main(String[] args) throws IOException, NamingException {

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");

Person person = applicationContext.getBean(Person.class);

person.setHungry(0);

person.setName("admin");

person.isNeedEat();

}

}

View Code

  执行Main方法后,我们可以看到如下结果:

Image

二、SpringApplication启动分析

 2.1、SpringApplication初始化分析

 在这里我们先追踪一下SpringApplication.run的方法:

/**

* Static helper that can be used to run a {@link SpringApplication} from the

* specified sources using default settings and user supplied arguments.

* @param sources the sources to load

* @param args the application arguments (usually passed from a Java main method)

* @return the running {@link ApplicationContext}

*/

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {

return new SpringApplication(sources).run(args);

}

  该方法会创建SpringApplication对象,我们继续看一下关键代码:

 

//......

/**

* Create a new {@link SpringApplication} instance. The application context will load

* beans from the specified sources (see {@link SpringApplication class-level}

* documentation for details. The instance can be customized before calling

* {@link #run(String...)}.

* @param sources the bean sources

* @see #run(Object, String[])

* @see #SpringApplication(ResourceLoader, Object...)

*/

public SpringApplication(Object... sources) {

initialize(sources);

}

//......

@SuppressWarnings({ "unchecked", "rawtypes" })

private void initialize(Object[] sources) {

if (sources != null && sources.length > 0) {

this.sources.addAll(Arrays.asList(sources));

}

this.webEnvironment = deduceWebEnvironment();

setInitializers((Collection) getSpringFactoriesInstances(

ApplicationContextInitializer.class));

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

this.mainApplicationClass = deduceMainApplicationClass();

}

  这里大家重点关注一下源代码中getSpringFacoriesInstances方法, ApplicationListener接口,ApplicationContextInitializer接口,这些接口都是通过SpringFactoriesLoader从META-INF/spring.factories文件里加载的

  其中getSpringFactoriesInstances的关键代码:

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,

Class<?>[] parameterTypes, Object... args) {

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

// Use names and ensure unique to protect against duplicates

Set<String> names = new LinkedHashSet<String>(

SpringFactoriesLoader.loadFactoryNames(type, classLoader));

List<T> instances = createSpringFactoriesInstances(type, parameterTypes,

classLoader, args, names);

AnnotationAwareOrderComparator.sort(instances);

return instances;

}

  这里面有一个关键类叫做SpringFactoriesLoader 该类的主要作用是读取META-INF/spring.factories配置文件里配置的引导对象,我们来看一下代码:

ImageImage

/*

* Copyright 2002-2016 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.core.io.support;

import java.io.IOException;

import java.lang.reflect.Constructor;

import java.net.URL;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Enumeration;

import java.util.List;

import java.util.Properties;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;

import org.springframework.core.io.UrlResource;

import org.springframework.util.Assert;

import org.springframework.util.ClassUtils;

import org.springframework.util.ReflectionUtils;

import org.springframework.util.StringUtils;

/**

* General purpose factory loading mechanism for internal use within the framework.

*

* <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates

* factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which

* may be present in multiple JAR files in the classpath. The {@code spring.factories}

* file must be in {@link Properties} format, where the key is the fully qualified

* name of the interface or abstract class, and the value is a comma-separated list of

* implementation class names. For example:

*

* <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>

*

* where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1}

* and {@code MyServiceImpl2} are two implementations.

*

* @author Arjen Poutsma

* @author Juergen Hoeller

* @author Sam Brannen

* @since 3.2

*/

public abstract class SpringFactoriesLoader {

private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

/**

* The location to look for factories.

* <p>Can be present in multiple JAR files.

*/

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

/**

* Load and instantiate the factory implementations of the given type from

* {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.

* <p>The returned factories are sorted in accordance with the {@link AnnotationAwareOrderComparator}.

* <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}

* to obtain all registered factory names.

* @param factoryClass the interface or abstract class representing the factory

* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)

* @see #loadFactoryNames

* @throws IllegalArgumentException if any factory implementation class cannot

* be loaded or if an error occurs while instantiating any factory

*/

public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {

Assert.notNull(factoryClass, "'factoryClass' must not be null");

ClassLoader classLoaderToUse = classLoader;

if (classLoaderToUse == null) {

classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();

}

List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);

if (logger.isTraceEnabled()) {

logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);

}

List<T> result = new ArrayList<T>(factoryNames.size());

for (String factoryName : factoryNames) {

result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));

}

AnnotationAwareOrderComparator.sort(result);

return result;

}

/**

* Load the fully qualified class names of factory implementations of the

* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given

* class loader.

* @param factoryClass the interface or abstract class representing the factory

* @param classLoader the ClassLoader to use for loading resources; can be

* {@code null} to use the default

* @see #loadFactories

* @throws IllegalArgumentException if an error occurs while loading factory names

*/

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {

String factoryClassName = factoryClass.getName();

try {

Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :

ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

List<String> result = new ArrayList<String>();

while (urls.hasMoreElements()) {

URL url = urls.nextElement();

Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));

String factoryClassNames = properties.getProperty(factoryClassName);

result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));

}

return result;

}

catch (IOException ex) {

throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +

"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);

}

}

@SuppressWarnings("unchecked")

private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {

try {

Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);

if (!factoryClass.isAssignableFrom(instanceClass)) {

throw new IllegalArgumentException(

"Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");

}

Constructor<?> constructor = instanceClass.getDeclaredConstructor();

ReflectionUtils.makeAccessible(constructor);

return (T) constructor.newInstance();

}

catch (Throwable ex) {

throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex);

}

}

}

View Code

  这里面的ApplicationListener略微特殊,它被定义到META-INF/spring.factories里,该监听器主要监听SpringApplicationEvent事件,SpringApplicationEvent有如下子类:

   1. ApplicationStartingEvent

   2. ApplicationEnvironmentPreparedEvent

   3. ApplicationPreparedEvent

   4. ApplicationFailedEvent

   5. ApplicationReadyEvent

  ApplicationContextInitializer该接口

ImageImage

/*

* Copyright 2002-2011 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.context;

/**

* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}

* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.

*

* <p>Typically used within web applications that require some programmatic initialization

* of the application context. For example, registering property sources or activating

* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()

* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support

* for declaring a "contextInitializerClasses" context-param and init-param, respectively.

*

* <p>{@code ApplicationContextInitializer} processors are encouraged to detect

* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been

* implemented or if the @{@link org.springframework.core.annotation.Order Order}

* annotation is present and to sort instances accordingly if so prior to invocation.

*

* @author Chris Beams

* @since 3.1

* @see org.springframework.web.context.ContextLoader#customizeContext

* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM

* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses

* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers

*/

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

/**

* Initialize the given application context.

* @param applicationContext the application to configure

*/

void initialize(C applicationContext);

}

View Code

  该接口doc文档上描述很清楚了,在调用ConfigurableApplicationContext的refresh()之前进行的初始化操作,比如说:激活profile , 注册PropertySource , FrameworkServlet(DispacherServlet的父类)设置 contextConfigLocation等。

2.2、SpringApplication的run方法分析

  这里我贴一下关键代码:

/**

* Run the Spring application, creating and refreshing a new

* {@link ApplicationContext}.

* @param args the application arguments (usually passed from a Java main method)

* @return a running {@link ApplicationContext}

*/

public ConfigurableApplicationContext run(String... args) {

StopWatch stopWatch = new StopWatch();

stopWatch.start();

ConfigurableApplicationContext context = null;

FailureAnalyzers analyzers = null;

configureHeadlessProperty();

SpringApplicationRunListeners listeners = getRunListeners(args);

listeners.starting();

try {

ApplicationArguments applicationArguments = new DefaultApplicationArguments(

args);

ConfigurableEnvironment environment = prepareEnvironment(listeners,

applicationArguments);

Banner printedBanner = printBanner(environment);

context = createApplicationContext();

analyzers = new FailureAnalyzers(context);

prepareContext(context, environment, listeners, applicationArguments,

printedBanner);

refreshContext(context);

afterRefresh(context, applicationArguments);

listeners.finished(context, null);

stopWatch.stop();

if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass)

.logStarted(getApplicationLog(), stopWatch);

}

return context;

}

catch (Throwable ex) {

handleRunFailure(context, listeners, analyzers, ex);

throw new IllegalStateException(ex);

}

}

1. 获取SpringApplicationRunListener

private SpringApplicationRunListeners getRunListeners(String[] args) {

Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };

return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(

SpringApplicationRunListener.class, types, this, args));

}

  该接口首先从META-INF/spring.factories文件里获取所有配置的SpringApplicationRunner ,那么这个接口时干啥的呢?我们来看一下源代码:

ImageImage

/*

* Copyright 2012-2016 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.boot;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ConfigurableApplicationContext;

import org.springframework.core.env.ConfigurableEnvironment;

import org.springframework.core.io.support.SpringFactoriesLoader;

/**

* Listener for the {@link SpringApplication} {@code run} method.

* {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}

* and should declare a public constructor that accepts a {@link SpringApplication}

* instance and a {@code String[]} of arguments. A new

* {@link SpringApplicationRunListener} instance will be created for each run.

*

* @author Phillip Webb

* @author Dave Syer

*/

public interface SpringApplicationRunListener {

/**

* Called immediately when the run method has first started. Can be used for very

* early initialization.

*/

void starting();

/**

* Called once the environment has been prepared, but before the

* {@link ApplicationContext} has been created.

* @param environment the environment

*/

void environmentPrepared(ConfigurableEnvironment environment);

/**

* Called once the {@link ApplicationContext} has been created and prepared, but

* before sources have been loaded.

* @param context the application context

*/

void contextPrepared(ConfigurableApplicationContext context);

/**

* Called once the application context has been loaded but before it has been

* refreshed.

* @param context the application context

*/

void contextLoaded(ConfigurableApplicationContext context);

/**

* Called immediately before the run method finishes.

* @param context the application context or null if a failure occurred before the

* context was created

* @param exception any run exception or null if run completed successfully.

*/

void finished(ConfigurableApplicationContext context, Throwable exception);

}

View Code

  其实简单点来说就是在SpringBoot启动过程中各个阶段需要做的事情,阶段包括:程序准备启动,准备环境,ApplicationContext准备加载,程序启动完成等等。

  其中该接口默认有一个实现类EventPublishingRunListener至关重要大家需要了解一下:

ImageImage

/*

* Copyright 2012-2017 the original author or authors.

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package org.springframework.boot.context.event;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.SpringApplicationRunListener;

import org.springframework.context.ApplicationContextAware;

import org.springframework.context.ApplicationListener;

import org.springframework.context.ConfigurableApplicationContext;

import org.springframework.context.event.ApplicationEventMulticaster;

import org.springframework.context.event.SimpleApplicationEventMulticaster;

import org.springframework.context.support.AbstractApplicationContext;

import org.springframework.core.Ordered;

import org.springframework.core.env.ConfigurableEnvironment;

import org.springframework.util.ErrorHandler;

/**

* {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.

* <p>

* Uses an internal {@link ApplicationEventMulticaster} for the events that are fired

* before the context is actually refreshed.

*

* @author Phillip Webb

* @author Stephane Nicoll

*/

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

private final SpringApplication application;

private final String[] args;

private final SimpleApplicationEventMulticaster initialMulticaster;

public EventPublishingRunListener(SpringApplication application, String[] args) {

this.application = application;

this.args = args;

this.initialMulticaster = new SimpleApplicationEventMulticaster();

for (ApplicationListener<?> listener : application.getListeners()) {

this.initialMulticaster.addApplicationListener(listener);

}

}

@Override

public int getOrder() {

return 0;

}

@Override

@SuppressWarnings("deprecation")

public void starting() {

this.initialMulticaster

.multicastEvent(new ApplicationStartedEvent(this.application, this.args));

}

@Override

public void environmentPrepared(ConfigurableEnvironment environment) {

this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(

this.application, this.args, environment));

}

@Override

public void contextPrepared(ConfigurableApplicationContext context) {

}

@Override

public void contextLoaded(ConfigurableApplicationContext context) {

for (ApplicationListener<?> listener : this.application.getListeners()) {

if (listener instanceof ApplicationContextAware) {

((ApplicationContextAware) listener).setApplicationContext(context);

}

context.addApplicationListener(listener);

}

this.initialMulticaster.multicastEvent(

new ApplicationPreparedEvent(this.application, this.args, context));

}

@Override

public void finished(ConfigurableApplicationContext context, Throwable exception) {

SpringApplicationEvent event = getFinishedEvent(context, exception);

if (context != null && context.isActive()) {

// Listeners have been registered to the application context so we should

// use it at this point if we can

context.publishEvent(event);

}

else {

// An inactive context may not have a multicaster so we use our multicaster to

// call all of the context's listeners instead

if (context instanceof AbstractApplicationContext) {

for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)

.getApplicationListeners()) {

this.initialMulticaster.addApplicationListener(listener);

}

}

if (event instanceof ApplicationFailedEvent) {

this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());

}

this.initialMulticaster.multicastEvent(event);

}

}

private SpringApplicationEvent getFinishedEvent(

ConfigurableApplicationContext context, Throwable exception) {

if (exception != null) {

return new ApplicationFailedEvent(this.application, this.args, context,

exception);

}

return new ApplicationReadyEvent(this.application, this.args, context);

}

private static class LoggingErrorHandler implements ErrorHandler {

private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

@Override

public void handleError(Throwable throwable) {

logger.warn("Error calling ApplicationEventListener", throwable);

}

}

}

View Code

  这个类里面有一个SimpleApplicationEventMulticaster的属性,根据前面分析,该属性就是执行关于SpringApplicationEvent的事件监听方法的。该类最主要作用就是通知各个阶段的listener处理对应阶段的事件

 

2、调用所有的SpringApplicationRunListenner的start方法

  我们可以看一下SpringApplicationRunListeners类里的方法:

public void starting() {

for (SpringApplicationRunListener listener : this.listeners) {

listener.starting();

}

}

  

3、执行prepareEnvironment方法

public void environmentPrepared(ConfigurableEnvironment environment) {

for (SpringApplicationRunListener listener : this.listeners) {

listener.environmentPrepared(environment);

}

}

4、根据当前的Environment打印Banner

5、创建ConfigurableApplicationContext对象与FailureAnalyzers

  在这里会根据this.webEnvironment的属性值来确定创建的ApplicationContext对象:

  

/**

* Strategy method used to create the {@link ApplicationContext}. By default this

* method will respect any explicitly set application context or application context

* class before falling back to a suitable default.

* @return the application context (not yet refreshed)

* @see #setApplicationContextClass(Class)

*/

protected ConfigurableApplicationContext createApplicationContext() {

Class<?> contextClass = this.applicationContextClass;

if (contextClass == null) {

try {

contextClass = Class.forName(this.webEnvironment

? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);

}

catch (ClassNotFoundException ex) {

throw new IllegalStateException(

"Unable create a default ApplicationContext, "

+ "please specify an ApplicationContextClass",

ex);

}

}

return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);

}

  如果是web环境那就创建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext ,否则就创建org.springframework.context.annotation.AnnotationConfigApplicationContext

6、调用prepareContext方法

public void contextPrepared(ConfigurableApplicationContext context) {

for (SpringApplicationRunListener listener : this.listeners) {

listener.contextPrepared(context);

}

}

7、调用 refreshContext方法

  该方法最终会执行AbstractApplicationContext的refresh()方法,我在这里贴一下源代码

ImageImage

@Override

public void refresh() throws BeansException, IllegalStateException {

synchronized (this.startupShutdownMonitor) {

// Prepare this context for refreshing.

prepareRefresh();

// Tell the subclass to refresh the internal bean factory.

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.

prepareBeanFactory(beanFactory);

try {

// Allows post-processing of the bean factory in context subclasses.

postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.

invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.

registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.

initMessageSource();

// Initialize event multicaster for this context.

initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.

onRefresh();

// Check for listener beans and register them.

registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.

finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.

finishRefresh();

}

catch (BeansException ex) {

if (logger.isWarnEnabled()) {

logger.warn("Exception encountered during context initialization - " +

"cancelling refresh attempt: " + ex);

}

// Destroy already created singletons to avoid dangling resources.

destroyBeans();

// Reset 'active' flag.

cancelRefresh(ex);

// Propagate exception to caller.

throw ex;

}

finally {

// Reset common introspection caches in Spring's core, since we

// might not ever need metadata for singleton beans anymore...

resetCommonCaches();

}

}

}

View Code

  在这个方法里会初始化BeanFactory 初始化BeanFactoryPostProcessor 注册BeanPostProcessor 初始化MessageSource 注册事件监听器等操作。建议大家深入了解Spring的IOC加载原理

8、执行afterRefresh()

/**

* Called after the context has been refreshed.

* @param context the application context

* @param args the application arguments

*/

protected void afterRefresh(ConfigurableApplicationContext context,

ApplicationArguments args) {

callRunners(context, args);

}

private void callRunners(ApplicationContext context, ApplicationArguments args) {

List<Object> runners = new ArrayList<Object>();

runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());

runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());

AnnotationAwareOrderComparator.sort(runners);

for (Object runner : new LinkedHashSet<Object>(runners)) {

if (runner instanceof ApplicationRunner) {

callRunner((ApplicationRunner) runner, args);

}

if (runner instanceof CommandLineRunner) {

callRunner((CommandLineRunner) runner, args);

}

}

}

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {

try {

(runner).run(args);

}

catch (Exception ex) {

throw new IllegalStateException("Failed to execute ApplicationRunner", ex);

}

}

 

  该方法会从IOC容器里找到ApplicationRunner或者CommandLineRunner并执行其run方法,当我们需要在SpringBoot程序启动时处理我们自己的逻辑,那么就可以实现上述接口

9、调用 listeners.finished方法

public void finished(ConfigurableApplicationContext context, Throwable exception) {

for (SpringApplicationRunListener listener : this.listeners) {

callFinishedListener(listener, context, exception);

}

}

private void callFinishedListener(SpringApplicationRunListener listener,

ConfigurableApplicationContext context, Throwable exception) {

try {

listener.finished(context, exception);

}

catch (Throwable ex) {

if (exception == null) {

ReflectionUtils.rethrowRuntimeException(ex);

}

if (this.log.isDebugEnabled()) {

this.log.error("Error handling failed", ex);

}

else {

String message = ex.getMessage();

message = (message == null ? "no error message" : message);

this.log.warn("Error handling failed (" + message + ")");

}

}

}

10、启动时的异常处理

private void handleRunFailure(ConfigurableApplicationContext context,

SpringApplicationRunListeners listeners, FailureAnalyzers analyzers,

Throwable exception) {

try {

try {

handleExitCode(context, exception);

listeners.finished(context, exception);

}

finally {

reportFailure(analyzers, exception);

if (context != null) {

context.close();

}

}

}

catch (Exception ex) {

logger.warn("Unable to close ApplicationContext", ex);

}

ReflectionUtils.rethrowRuntimeException(exception);

}

  我们可以看到在SpringApplicationRunnerListener的作用至关重要,几乎每做一件事情都涉及到此接口的方法 ,另外 EventPublishingRunListener会在各个阶段通知各个listener处理启动周期内各个阶段性事件

三、测试示例

  通过在META-INF/spring.factories里配置引导类,来验证一下我们上述分析的启动过程

  1、创建MyBootStrapApplicationListener示例:

ImageImage

package com.hzgj.lyrk.member.applicationlistener;

import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;

import org.springframework.context.ApplicationListener;

/**

* META-INF/spring.factories 配置的listener测试

* @author chen.nie

* @date 2018/4/26

**/

public class MyBootStrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

@Override

public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {

System.out.println("BootStrapApplicationListener");

}

}

View Code

  2、创建MyCommandRunner

ImageImage

package com.hzgj.lyrk.member.commandlinerunner;

import org.springframework.boot.CommandLineRunner;

import org.springframework.stereotype.Component;

/**

* commandLineRunner测试

*

* @author chen.nie

* @date 2018/4/26

**/

@Component

public class MyCommandRunner implements CommandLineRunner {

@Override

public void run(String... args) throws Exception {

System.out.println("MyCommandRunner execute .....");

}

}

View Code

  3、在spring.factories配置ApplicationListener

org.springframework.context.ApplicationListener=\

com.hzgj.lyrk.member.applicationlistener.MyBootStrapApplicationListener

  当我们启动SpringBoot项目时,可以发现如下结果:

Image

Image

我们可以发现我们配置的ApplicationListener在最开始就会执行,而CommandLineRunner在最后才执行

四、SpringBoot启动总结

  1. SpringBoot启动时SpringApplicationRunListener接口的相关方法至关重要,它定义了启动时的各个“时间点”。

  2. SpringBoot可以从spring.factoies文件里读取配置的ApplicationListener

  3. META-INF文件夹下的spring.factoies文件是SpringBoot启动的核心文件,SpringFatoriesLoader会读取该文件夹下的相关配置作为引导

  4. SpringBoot启动时利用了事件机制,来发送启动时各个周期阶段的事件

本文内容总结:一、基于Springframework的事件机制,1.1、JDK中的事件接口与类,1.2、spring中的事件类,1.3、基于Spring的自定义事件,二、SpringApplication启动分析, 2.1、SpringApplication初始化分析,2.2、SpringApplication的run方法分析,1. 获取SpringApplicationRunListener,2、调用所有的SpringApplicationRunListenner的start方法,3、执行prepareEnvironment方法,4、根据当前的Environment打印Banner,5、创建ConfigurableApplicationContext对象与FailureAnalyzers,6、调用prepareContext方法,7、调用 refreshContext方法,8、执行afterRefresh(),9、调用 listeners.finished方法,10、启动时的异常处理,三、测试示例,  1、创建MyBootStrapApplicationListener示例:,  2、创建MyCommandRunner,  3、在spring.factories配置ApplicationListener,四、SpringBoot启动总结,

原文链接:https://www.cnblogs.com/niechen/p/8947973.html

以上是 深入理解SpringBoot之启动探究 的全部内容, 来源链接: utcz.com/z/296616.html

回到顶部