Spring Boot 源码研读之执行SpringApplicationRunListener.starting()

源码分析

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 实现类只有一个 EventPublishingRunListenerEventPublishingRunListener 类的主要作用的是将生命周期的每个操作包装成一个对应的 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 容器广播的。

总结一下

  1. SpringApplicationRunListener 定义了 Spring boot 应用生命周期各阶段的方法,包含starting、environmentPrepared、contextPrepared、contextLoaded、started、ready、failed
  2. EventPublishingRunListener 实现了 SpringApplicationRunListener生命周期方法,内部通过 SimpleApplicationEventMulticaster 事件广播器广播执行实现了对应事件的 ApplicationListener
  3. ApplicationListener 实现类是具体的事件实现者,用户可以自定义实现类并结合Spring内置的事件以便在其生命周期各个阶段做一些额外的操作。