源码分析
java
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
//....
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
通过上面代码可以看出,核心逻辑就一行代码:this .listeners.forEach(listenerAction ); 即通过**λ** 表达式的方式去执行 SpringApplicationRunListener 实现类的 starting() 方法,入参是 ConfigurableBootstrapContext 对象,即为 Spring Boot 源码研读之创建DefaultBootstrapContext并执行BootstrapRegistryInitializer.initialize()-CSDN博客 中创建的DefaultBootstrapContext对象。
而通过 SpringFactoriesLoader 加载的 SpringApplicationRunListener 实现类只有一个 EventPublishingRunListener。EventPublishingRunListener 类的主要作用的是将生命周期的每个操作包装成一个对应的 ApplicationEvent 事件,并通过 SimpleApplicationEventMulticaster 去多播执行所有的 ApplicationListener 实现类的 onApplicationEvent() 方法。
注意这里会根据传入的事件类型进行过滤,即传入的事件类型是否与ApplicationListener 实现类 onApplicationEvent() 方法参数的事件类型一致,如果不一致会被过滤掉。注意:事件类型的判断也可以在 onApplicationEvent() 方法的内部判断,即实现类 onApplicationEvent() 方法参数类型为抽象类ApplicationEvent。
多播事件前的刷新ApplicationListener
java
private void refreshApplicationListeners() {
this.application.getListeners().forEach(this.initialMulticaster::addApplicationListener);
}
java
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.defaultRetriever) {
// Explicitly remove target for a proxy, if registered already,
// in order to avoid double invocations of the same listener.
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
目的是将应用配置中的 listeners 注册到广播器中,避免有遗漏或者后续新添加,同时还进行了aop代理去重,避免了listener的重复调用。
上下文加载完毕时的特殊处理
java
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware contextAware) {
contextAware.setApplicationContext(context);
}
context.addApplicationListener(listener);
}
multicastInitialEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
在上下文加载完毕,广播 ApplicationPreparedEvent 事件前会对实现了ApplicationContextAware接口的listener进行上下文填充,同时将listener添加到Spring容器的广播器中,这样容器便有了广播事件的能力。
我们可以看到在后续的 started、ready、failed 节点时,广播事件都是通过spring 容器广播的。
总结一下
SpringApplicationRunListener定义了 Spring boot 应用生命周期各阶段的方法,包含starting、environmentPrepared、contextPrepared、contextLoaded、started、ready、failedEventPublishingRunListener实现了SpringApplicationRunListener生命周期方法,内部通过SimpleApplicationEventMulticaster事件广播器广播执行实现了对应事件的ApplicationListener。ApplicationListener实现类是具体的事件实现者,用户可以自定义实现类并结合Spring内置的事件以便在其生命周期各个阶段做一些额外的操作。