上一篇:Spring Developer Tools 源码分析:四、类加载器。
本篇是下一篇 Restarter 的前置内容,这里介绍的 ApplicationListener 事件触发过程是针对整个 Spring Boot 的过程。
Spring Developer Tools 通过 ApplicationListener
不同阶段的事件来控制 Restarter
的运行。Restarter
包含了获取 main
方法类,初始化监控资源路径等功能。
devtools 项目的 "META-INF/spring.factories"
文件中,和这里相关的配置如下:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.devtools.restart.RestartApplicationListener
RestartApplicationListener
类定义如下(使用了最高的优先级):
public class RestartApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {
接口实现方法如下:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationStartingEvent) {
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
if (event instanceof ApplicationReadyEvent
|| event instanceof ApplicationFailedEvent) {
Restarter.getInstance().finish();
}
if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent((ApplicationFailedEvent) event);
}
}
在这里可以看到 RestartApplicationListener
会监控 4 类事件,分别如下:
- ①
ApplicationStartingEvent
启动时,这个事件会尽可能早的触发。 - ②
ApplicationPreparedEvent
:ApplicationContext
已完全准备但未刷新(refresh) 时发布的事件。bean 的定义将会被加载,并且Environment
已经可以在这个阶段使用了。 - ③
ApplicationReadyEvent
已经准备好提供服务。 - ④
ApplicationFailedEvent
启动失败。
下面用尽可能短的代码和语言来说明这些事件的触发过程。
事件触发过程
在 SpringApplication
构造方法中,有如下代码:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//省略其他
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//省略其他
}
这里会从 "META-INF/spring.factories"
文件夹获取所有 ApplicationListener
类的实现,由于 Spring 还没启动,所以这里只能通过读取资源文件来获取一些启动更早的接口。获取所有的实现接口后,会 set
到当前类的 listeners
中。在这一步,就能获取到 RestartApplicationListener
。
接下来是 SpringApplication
的 run
方法。
public ConfigurableApplicationContext run(String... args) {
//省略部分
SpringApplicationRunListeners listeners = getRunListeners(args);//1
listeners.starting();//ApplicationStartingEvent - ①
try {
//省略
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);//ApplicationEnvironmentPreparedEvent
//省略
context = createApplicationContext();
//省略
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);//ApplicationPreparedEvent - ②
refreshContext(context);
//省略
listeners.started(context);//ApplicationStartedEvent
//省略
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);//ApplicationFailedEvent - ④
throw new IllegalStateException(ex);
}
try {
listeners.running(context);//ApplicationReadyEvent - ③
}
//省略
return context;
}
上面代码只留下了和 listeners
相关的主干代码。你可以发现这里都是和 SpringApplicationRunListeners
有关的代码,和前面提到的 ApplicationListener
不是一个接口,这之间是什么关系呢。
在 //1
处,通过 getRunListeners
获取的 SpringApplicationRunListeners
(代理类),从这个方法来看,这里也是从 spring-boot 项目下的 "META-INF/spring.factories"
获取的所有的 SpringApplicationRunListener
实现类。在这个配置文件中有下面的配置:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
在 SpringApplicationRunListeners
中会实际执行所有 runlistener 的方法。在 EventPublishingRunListener
中,会通过 SpringApplication.getListeners()
获取一开始的 listener
,在后续触发事件时会执行。除此之外还要特别注意 EventPublishingRunListener
中的 contextLoaded
方法,代码如下:
@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));
}
这里有两个特殊处理,首先如果实现了 ApplicationContextAware
接口,就会在此时去调用该方法。还有就是会把 listener
添加到已经创建的 context
中,添加到 context
是非常重要的一个步骤。EventPublishingRunListener
只会在 Spring Boot 启动过程中处理事件,当 run
方法执行完毕后,后续的事件都是在 context
范围内的,这里加入 context
后,就能处理其他感兴趣的事件了。
前面自动配置中介绍的
@EventListener
会在所有单例的 bean 创建完成后,通过EventListenerMethodProcessor
处理注解形式的事件监听,这些方法也都会通过代理转换为ApplicationListener
,然后加入到context
中。
具体哪个阶段触发哪个事件,大家可以根据上面的主干代码去细看(代码注释已经标明了会触发的事件),下一篇回到 Restarter
继续。