2020-11-23 | Java | Unlock

SpringBoot源码分析(9)-SpringBoot的事件监听机制

上一篇分析了SpringApplication对象的构建过程及SpringBoot自己实现的一套SPI机制,关键步骤总结:

  1. SpringApplication对象的构造过程其实就是给SpringApplication类的6个成员变量赋值;
  2. SpringBoot通过以下步骤实现自己的SPI机制:
  • 1)首先获取线程上下文类加载器;
  • 2)然后利用上下文类加载器从spring.factories配置文件中加载所有的SPI扩展实现类并放入缓存中;
  • 3)根据SPI接口从缓存中取出相应的SPI扩展实现类;
  • 4)实例化从缓存中取出的SPI扩展实现类并返回。

1.SpringBoot广播内置生命周期事件流程分析

在SpringBoot启动过程中,每个不同的启动阶段会分别广播不同的内置生命周期事件,然后相应的监听器会监听这些事件来执行一些初始化逻辑工作。比如ConfigFileApplicationListener会监听onApplicationEnvironmentPreparedEvent事件来加载配置文件application.properties的环境变量等。

为了探究SpringBoot广播内置生命周期事件流程,回顾一下SpringBoot的启动流程代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 【0】新建一个SpringApplicationRunListeners对象用于触发SpringBoot启动过程中的生命周期事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 【1】》》》》》触发【ApplicationStartingEvent】事件,标志SpringApplication开始启动
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 【2】》》》》》触发【ApplicationEnvironmentPreparedEvent】事件,此时会去加载application.properties等配置文件的环境变量,同时也有标志环境变量已经准备好的意思
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 【3】》》》》》触发【ApplicationContextInitializedEvent】事件,标志context容器被创建且已准备好
// 【4】》》》》》触发【ApplicationPreparedEvent】事件,标志Context容器已经准备完成
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 【5】》》》》》触发【ApplicationStartedEvent】事件,标志spring容器已经刷新,此时所有的bean实例都已经加载完毕
listeners.started(context);
callRunners(context, applicationArguments);
}
// 【6】》》》》》触发【ApplicationFailedEvent】事件,标志SpringBoot启动失败
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 【7】》》》》》触发【ApplicationReadyEvent】事件,标志SpringApplication已经正在运行即已经成功启动,可以接收服务请求了。
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

可以看到SpringBoot在启动过程中首先会先新建一个SpringApplicationRunListeners对象用于触发SpringBoot启动过程中的各种生命周期事件,比如触发ApplicationStartingEvent,ApplicationEnvironmentPreparedEventApplicationContextInitializedEvent等事件,然后相应的监听器会执行一些SpringBoot启动过程中的初始化逻辑。那么,监听这些SpringBoot的生命周期事件的监听器们是何时被加载实例化的呢?根据之前的分析SpringApplication的构建过程吗?这些执行初始化逻辑的监听器们正是在SpringApplication的构建过程中根据ApplicationListener接口去spring.factories配置文件中加载并实例化的。

1.1 为广播SpringBoot内置生命周期事件做前期准备

1.1.1 加载ApplicationListener监听器实现类

前面分析到,在构建SpringApplication对象时的setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));这句代码做的事情就是从spring.factories中加载出ApplicationListener事件监听接口的SPI扩展实现类然后添加到SpringApplication对象的listeners集合中,用于后续监听SpringBoot启动过程中的事件,来执行一些初始化逻辑工作。

SpringBoot启动时的具体监听器们都实现了ApplicationListener接口,其在spring.factories部分配置如下:

spring-boot-2.1.0.RELEASE\spring-boot-project\spring-boot\src\main\resources\META-INF\spring.factories

1
2
3
4
5
6
7
8
9
10
11
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

1.1.2 加载SPI扩展类EventPublishingRunListener

前面讲到,在SpringBoot的启动过程中首先会先新建一个SpringApplicationRunListeners对象用于触发SpringBoot启动过程中的生命周期事件,来看下SpringApplicationRunListeners listeners = getRunListeners(args);这句代码:

1
2
3
4
5
6
7
8
private SpringApplicationRunListeners getRunListeners(String[] args) {
// 构造一个由SpringApplication.class和String[].class组成的types
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 1) 根据SpringApplicationRunListener接口去spring.factories配置文件中加载其SPI扩展实现类
// 2) 构建一个SpringApplicationRunListeners对象并返回
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

将重点放到getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)这句代码,getSpringFactoriesInstances这个方法在之前分析SpringBoot的SPI机制时已经详细分析过。可以看到SpringBoot此时又是根据SpringApplicationRunListener这个SPI接口去spring.factories中加载相应的SPI扩展实现类,直接去spring.factories中看看SpringApplicationRunListener有哪些SPI实现类:

1
2
3
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

可以看到,SpringApplicationRunListener只有EventPublishingRunListener这个SPI实现类。

EventPublishingRunListener在SpringBoot启动过程的不同阶段触发不同的SpringBoot的生命周期事件,SpringApplicationRunListeners对象没有承担广播事件的职责,而最终是委托EventPublishingRunListener来广播事件的。

因为从spring.factories中加载EventPublishingRunListener类后还会实例化该类,那么再跟进EventPublishingRunListener的源码,看看其是如何承担触发SpringBoot生命周期事件这一职责的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

private final SpringApplication application;

private final String[] args;

//拥有一个SimpleApplicationEventMulticaster事件广播器来广播事件
private final SimpleApplicationEventMulticaster initialMulticaster;

public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 新建一个事件广播器SimpleApplicationEventMulticaster对象
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 遍历在构造SpringApplication对象时从spring.factories配置文件中获取的事件监听器
for (ApplicationListener<?> listener : application.getListeners()) {
// 将从spring.factories配置文件中获取的事件监听器们添加到事件广播器initialMulticaster对象的相关集合中
this.initialMulticaster.addApplicationListener(listener);
}
}

@Override
public int getOrder() {
return 0;
}

// 》》》》》触发【ApplicationStartingEvent】事件
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

// 》》》》》触发【ApplicationEnvironmentPreparedEvent】事件
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

// 》》》》》触发【ApplicationContextInitializedEvent】事件
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}

// 》》》》》触发【ApplicationPreparedEvent】事件
@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));
}

// 》》》》》触发【ApplicationStartedEvent】事件
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}

// 》》》》》触发【ApplicationReadyEvent】事件
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}

// 》》》》》触发【ApplicationFailedEvent】事件
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, 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);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}

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);
}

}

}

可以看到EventPublishingRunListener类实现了SpringApplicationRunListener接口,SpringApplicationRunListener接口定义了SpringBoot启动时触发生命周期事件的接口方法,而EventPublishingRunListener类正是通过实现SpringApplicationRunListener接口的starting,environmentPreparedcontextPrepared等方法来广播SpringBoot不同的生命周期事件,直接看下SpringApplicationRunListener接口源码好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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);

/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
void started(ConfigurableApplicationContext context);

/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
void running(ConfigurableApplicationContext context);

/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
void failed(ConfigurableApplicationContext context, Throwable exception);

}

再接着分析EventPublishingRunListener这个类,可以看到其有一个重要的成员属性initialMulticaster,该成员属性是SimpleApplicationEventMulticaster类对象,该类正是承担了广播SpringBoot启动时生命周期事件的职责,EventPublishingRunListener对象没有承担广播事件的职责,而最终是委托SimpleApplicationEventMulticaster来广播事件的。

EventPublishingRunListener的源码中也可以看到在starting,environmentPreparedcontextPrepared等方法中也正是通过调用SimpleApplicationEventMulticaster类对象的multicastEvent方法来广播事件的。

思考 SpringBoot启动过程中触发事件时事件广播者是层层委托职责的,起初由SpringApplicationRunListeners对象承担,然后SpringApplicationRunListeners对象将广播事件职责委托给EventPublishingRunListener对象,最终EventPublishingRunListener对象将广播事件的职责委托给SimpleApplicationEventMulticaster对象。为什么要层层委托这么做呢?

前面讲到从spring.factories中加载出EventPublishingRunListener类后会实例化,而实例化必然会通过EventPublishingRunListener的构造函数来进行实例化,接下来分析下EventPublishingRunListener的构造函数源码:

1
2
3
4
5
6
7
8
9
10
11
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 新建一个事件广播器SimpleApplicationEventMulticaster对象
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 遍历在构造SpringApplication对象时从spring.factories配置文件中获取的事件监听器
for (ApplicationListener<?> listener : application.getListeners()) {
// 将从spring.factories配置文件中获取的事件监听器们添加到事件广播器initialMulticaster对象的相关集合中
this.initialMulticaster.addApplicationListener(listener);
}
}

可以看到在EventPublishingRunListener的构造函数中有一个for循环会遍历之前从spring.factories中加载的监听器们,然后添加到集合中缓存起来,用于以后广播各种事件时直接从这个集合中取出来即可,而不用再去spring.factories中加载,提高效率。

1.2 广播SpringBoot的内置生命周期事件

spring.factories配置文件中加载并实例化EventPublishingRunListener对象后,那么在SpringBoot的启动过程中会触发一系列SpringBoot内置的生命周期事件,从以上SpringBoot启动过程中的源码,可以看到在SpringBoot的启动过程中总共会触发7种不同类型的生命周期事件,来标志SpringBoot的不同启动阶段,同时,这些生命周期事件的监听器也会执行一些启动过程中的初始化逻辑。

以下是SpringBoot启动过程中要触发的事件类型,其中ApplicationFailedEvent在SpringBoot启动过程中遇到异常才会触发:

  1. ApplicationStartingEvent
  2. ApplicationEnvironmentPreparedEvent
  3. ApplicationContextInitializedEvent
  4. ApplicationPreparedEvent
  5. ApplicationStartedEvent
  6. ApplicationFailedEvent
  7. ApplicationReadyEvent

listeners.starting();这句代码为例,看看EventPublishingRunListener对象触发事件的源码:

1
2
3
4
5
6
7
8
public void starting() {
// 遍历listeners集合,这里实质取出的就是刚才从spring.factories中取出的SPI实现类EventPublishingRunListener
// 而EventPublishingRunListener对象承担了SpringBoot启动过程中负责广播不同的生命周期事件
for (SpringApplicationRunListener listener : this.listeners) {
// 调用EventPublishingRunListener的starting方法来广播ApplicationStartingEvent事件
listener.starting();
}
}

继续跟进listener.starting();的源码:

1
2
3
4
5
6
7
// 》》》》》触发【ApplicationStartingEvent】事件
@Override
public void starting() {
// EventPublishingRunListener对象将发布ApplicationStartingEvent这件事情委托给了initialMulticaster对象
// 调用initialMulticaster的multicastEvent方法来触发ApplicationStartingEvent事件
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

可以看到,EventPublishingRunListener对象将发布ApplicationStartingEvent这件事情委托给了SimpleApplicationEventMulticaster对象initialMulticaster,
,而initialMulticaster对象最终会调用其multicastEvent方法来触发ApplicationStartingEvent事件。

2.SpringBoot的内置生命周期事件总结

SpringBoot启动过程中要触发的各种生命周期事件

发布顺序 时间 用途
1 ApplicationStartingEvent 在SpringApplication启动时,在环境变量Environment或者容器ApplicationContext创建前触发,标志SpringApplication开始启动。
2 ApplicationEnvironmentPreparedEvent 当SpringApplication已经开始启动且环境变量Environment已经准备好时触发,标志环境变量已经准备好。
3 ApplicationContextInitializedEvent ApplicationContextInitializers的初始化方法已经被调用,即从spring.factories中加载的initializers已经执行ApplicationContext初始化逻辑但在bean定义加载前触发,标志ApplicationContext已经初始化完毕。
4 ApplicationPreparedEvent 在Spring容器刷新refresh前触发
5 ApplicationStartedEvent 在spring容器刷新后触发,但在调用ApplicationRunner和CommandLineRunner的run方法调用前触发,标志spring容器已经刷新,此时所有的bean实例等都已经加载了。
6 ApplicationReadyEvent 只要SpringApplication可以接收服务请求时即调用完ApplicationRunner和CommandLineRunner的run方法后触发,此时标志SpringApplication已经正在运行,即启动成功。
7 ApplicationFailedEvent 若SpringApplication未能成功启动时则会catch住异常发布ApplicationFailedEvent事件,标志ApplicationFailedEvent启动失败。

总结

SpringBoot启动过程中会触发7种类型的生命周期事件,标志不同的启动阶段,然后相应的监听器会监听这些事件来执行一些初始化逻辑工作。

评论加载中