在SpringBoot启动的整个过程中,有一个很重要的机制,叫事件监听机制。
这个机制如他的名字,在启动的各个阶段,SpringBoot会发布一些事件,而对应的监听器监听到事件之后会做相应的处理。
监听器机制的优点是,当一个事件发布后,会有不同的监听器来处理,如果用户想在这个事件发生时做其他处理,就可以添加一个新的监听器监听这个事件,就有了很强的扩展性。
那么接下来我们就来了解一下SpringBoot监听事件的机制和流程,还有SpringBoot启动过程中所有的事件类型,和事件发布后哪些监听器做了什么处理。
1. 获取SpringFactory中的Listener
众所周知,SpringBoot启动时需要调用SpringApplication.run方法,或者自己new出来一个SpringApplication对象之后调用run方法。
和之前的ApplicationContextInitializer相同,SpringBoot所需要的SpringFactory中所有的ApplicationListener也是在new SpringApplication对象时加载进来的。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
上述代码中倒数第二行,setListeners方法,就是将所有配置在spring.factories文件中,key值为 org.springframework.context.ApplicationListener
的类实例化后,设置到SpringApplication对象的listeners属性当中。
没有任何添加的情况下,会有11个Listener被实例化,分别从不同的spring依赖中获取:
-
spring-boot-autoconfigure
org.springframework.boot.autoconfigure.BackgroundPreinitializer -
spring-boot
org.springframework.boot.ClearCachesApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
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
以上的Listeners是真正监听到事件之后会去处理逻辑的监听器,但是想要发布事件并且让正确的监听器监听到对应事件,SpringApplicationRunListener是不可或缺的。
2. 获取SpringApplicationRunListener
在SpringApplication的核心,run方法之中,有一个getRunListeners方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
......
进入getRunListeners
方法:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
可以看到也是同样从SpringFactory获取到所有的以org.springframework.boot.SpringApplicationRunListener
为key值的RunListener,直接放进了new出来的SpringApplicationRunListeners对象之中。
默认情况下,SpringBoot只有一个ApplicationRunListener,就是在spring-boot
依赖中sping.factories定义的org.springframework.boot.context.event.EventPublishingRunListener
。
3. 发布事件
在SpringBoot启动过程中会在固定的阶段发布不同的时间,比如Starting事件,PrepareEnv事件,Started事件等等。这里我们用Starting事件举个例子:
在上面getRunListeners方法的下面一行 listeners.starting();
这里就是进入了SpringApplicationRunListeners中对应的方法。
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
在方法中我们可以看到,SpringApplicationRunListeners会遍历对象当中所有的RunListener,调用他们的starting方法。而在上一节中,我们已经知道默认情况下SpringBoot只会加载一个RunListener,就是org.springframework.boot.context.event.EventPublishingRunListener
。所以循环遍历就只有一个类,我们进入EventPublishingRunListener的starting方法看一下。
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
这里有一个this.initialMulticaster
,这是SpringBoot整个事件监听机制的核心之一,事件广播器,他是在什么时候被初始化的呢?我们看下面的code:
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);
}
}
这是EventPublishingRunListener的构造方法,我们已经知道在getRunListeners
方法中,EventPublishingRunListener被初始化成实例对象,在这个时候可以看到一个名为SimpleApplicationEventMulticaster的类被new出来赋给了EventPublishingRunListener的initialMulticaster属性。
我们再回到starting()方法当中,SimpleApplicationEventMulticaster的multicastEvent方法被调用,发布的事件是一个new出来的ApplicationStartingEvent事件。
接下来我们看一下发布一个事件的具体逻辑。
4. SimpleApplicationEventMulticaster的multicastEvent方法
核心逻辑如下:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
在这个方法中,有一个getApplicationListeners
方法,这个方法是广播事件最核心的一个逻辑。我们已经知道在最开始的时候,SpringApplication通过SpringFactoryLoader将11个ApplicationListener加载了进来,这个方法他的作用就是找到这11中所有关注了这个事件的监听器,之后就能遍历这些监听器去调用监听器里面的逻辑。
这里面的具体逻辑比较复杂,我在这里就不把源码贴出来了,感兴趣的同学可以自行阅读。我在这里用两张图总结一下其中的逻辑。
其中的核心在supportEventType这个方法当中,如图所示有两种情况:
- 当前ApplicationListener实现了SmartApplicationListener接口
1.1 判断supportEventType方法,如果支持该event,则进行下一步判断,否则直接返回false
1.2 如果supportEventType支持该event,继续判断supportSourceType,即判断是否支持当前入口函数对象 - 当前ApplicationListener未实现SmartApplicationListener接口,只需判断supportEventType,如果返回true,则该监听器支持当前event,否则不支持。
这里有个小问题,这些ApplicationListener为什么会继承不同的接口,他们之间有什么区别呢?
- 一般SpringBoot中的listener想要监听一个事件,会继承ApplicationListener接口,需要指定一个event的泛型,实现onApplicationEvent。泛型是为了指定这个监听器关注那个event事件,onApplicationEvent就是当监听到事件后的处理逻辑。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
- 如果在一个listener内想要监听多个事件,则需要继承SmartApplicationListener接口。SmartApplicationListener继承ApplicationListener并关注所有类型为ApplicationEvent的事件,所有使用他的时候必须实现supportEventType方法,去指定某几个event需要被你实现SmartApplicationListener关注。
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
/**
* Determine whether this listener actually supports the given event type.
* @param eventType the event type (never {@code null})
*/
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
/**
* Determine whether this listener actually supports the given source type.
* <p>The default implementation always returns {@code true}.
* @param sourceType the source type, or {@code null} if no source
*/
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
/**
* Determine this listener's order in a set of listeners for the same event.
* <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
*/
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}