在SpringBoot启动过程中,监听器(Listener) 是贯穿始终的核心组件,它负责监听启动生命周期中的关键节点(环境准备、容器刷新、启动完成等),并触发对应事件完成日志初始化、配置加载、缓存清理等核心工作。
很多新手看源码时会困惑:监听器从哪来?存在哪?什么时候被触发?事件怎么传递?本文结合SpringBoot 2.7.18 官方源码 ,从监听器加载→注册→事件产生→事件发布→事件监听全链路拆解,搭配实战案例,用最通俗的语言讲透底层原理。
一、先搞懂核心概念:两类监听器+事件机制
在开始源码分析前,先区分两个核心概念,避免混淆:
1. 两类核心监听器(关键!)
SpringBoot启动过程中,有两个层级的监听器,职责完全不同:
| 监听器类型 | 全限定类名 | 核心作用 | 数量 |
|---|---|---|---|
| 运行监听器 | SpringApplicationRunListener |
监听SpringBoot应用启动的整个生命周期,是启动流程的「总调度员」 | 固定1个:EventPublishingRunListener |
| 应用监听器 | ApplicationListener |
监听具体的Spring事件,是实际执行业务逻辑的「干活者」 | 多个(Spring内置+自定义) |
2. 核心工作流程(一句话总结)
- SpringBoot启动时,从
spring.factories加载运行监听器+应用监听器; - 运行监听器
EventPublishingRunListener持有所有应用监听器; - 启动到关键节点时,运行监听器产生对应事件;
- 运行监听器通过事件广播器发布事件;
- 对事件感兴趣的应用监听器执行自己的业务逻辑。
二、第一步:监听器从哪来?------ 加载流程(spring.factories)
SpringBoot不会硬编码监听器 ,而是通过SPI机制 从META-INF/spring.factories文件中加载所有监听器,这是SpringBoot的核心扩展规范。
1. 源码位置
我们看源码中getRunListeners和SpringApplication构造方法:
java
// SpringApplication.java 加载运行监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// SPI加载:SpringApplicationRunListener 接口的实现类
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
// SpringApplication构造方法:加载应用监听器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// ... 省略其他代码
// SPI加载:ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// SPI加载:ApplicationListener 接口的实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
}
2. spring.factories 配置文件
打开spring-boot-2.7.18.jar中的META-INF/spring.factories,能看到官方配置的监听器:
properties
# 1. 运行监听器:固定只有EventPublishingRunListener
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# 2. 应用监听器:多个内置监听器
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.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
3. 加载原理:getSpringFactoriesInstances
这个方法是SPI加载的核心:
- 读取所有jar包下的
spring.factories; - 根据接口全类名,找到对应的实现类全类名;
- 通过反射实例化对象;
- 排序后返回集合。
✅ 结论:
- 运行监听器:仅加载
EventPublishingRunListener; - 应用监听器:加载
spring.factories中配置的所有ApplicationListener实现类。
三、第二步:监听器存在哪?------ 注册流程
加载完成后,监听器会被注册到对应容器中,我们分两类看注册位置:
1. 应用监听器:注册到 SpringApplication 成员变量
源码中setListeners方法,将加载的所有ApplicationListener存入SpringApplication的listeners集合:
java
// 成员变量:存放所有应用监听器
private List<ApplicationListener<?>> listeners = new ArrayList<>();
// 注册应用监听器
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
// 获取所有应用监听器
public Set<ApplicationListener<?>> getListeners() {
return asUnmodifiableOrderedSet(this.listeners);
}
✅ 存储位置:SpringApplication对象的private List<ApplicationListener<?>> listeners。
2. 运行监听器:注册到 SpringApplicationRunListeners
getRunListeners方法返回SpringApplicationRunListeners对象,它内部持有运行监听器集合:
java
// SpringApplicationRunListeners 类
class SpringApplicationRunListeners {
private final Log logger;
// 持有所有运行监听器(实际只有EventPublishingRunListener)
private final List<SpringApplicationRunListener> listeners;
private final ApplicationStartup applicationStartup;
}
✅ 存储位置:SpringApplicationRunListeners的listeners集合。
四、第三步:核心桥梁!EventPublishingRunListener 绑定所有监听器
这是整个流程的核心枢纽 :EventPublishingRunListener在构造时,会把SpringApplication中所有应用监听器 注册到自己的事件广播器中。
直接看源码:
java
// EventPublishingRunListener.java 构造方法
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 1. 创建事件广播器:SimpleApplicationEventMulticaster
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 2. 关键!从SpringApplication中获取所有应用监听器,添加到广播器
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
核心作用总结
EventPublishingRunListener = 启动生命周期监听 + 事件广播转发:
- 监听SpringBoot启动的每一步;
- 持有所有应用监听器;
- 启动节点触发时,发布事件给应用监听器。
五、第四步:事件产生与发布 ------ 以环境准备为例
我们以SpringBoot启动中环境准备(environmentPrepared) 这个关键节点为例,完整走通事件产生→发布→监听流程。
1. 启动节点触发:运行监听器感知
SpringBoot启动执行到environmentPrepared时,SpringApplicationRunListeners会调用所有运行监听器的对应方法:
java
// SpringApplicationRunListeners.java
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
doWithListeners("spring.boot.application.environment-prepared",
(listener) -> listener.environmentPrepared(bootstrapContext, environment));
}
2. 产生事件:EventPublishingRunListener 创建事件
唯一的运行监听器EventPublishingRunListener,会创建对应生命周期事件:
java
// EventPublishingRunListener.java
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
// 1. 创建事件:ApplicationEnvironmentPreparedEvent(环境准备完成事件)
// 2. 通过事件广播器发布事件
this.initialMulticaster.multicastEvent(
new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
✅ 事件类型:ApplicationEnvironmentPreparedEvent(环境准备事件)。
3. 发布事件:SimpleApplicationEventMulticaster 广播事件
事件广播器SimpleApplicationEventMulticaster是事件发布的核心,它会遍历所有监听器,筛选对当前事件感兴趣的监听器并执行:
java
// SimpleApplicationEventMulticaster.java
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
// 1. 获取对当前事件感兴趣的监听器
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
// 2. 同步/异步执行监听器逻辑
if (executor != null) {
executor.execute(() -> this.invokeListener(listener, event));
} else {
this.invokeListener(listener, event);
}
}
}
// 最终执行监听器的onApplicationEvent方法
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 调用应用监听器的核心方法
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
// 忽略类型不匹配的异常
}
}
4. 筛选监听器:只通知感兴趣的监听器
广播器不会无脑通知所有监听器,而是通过supportsEvent方法筛选对当前事件感兴趣的监听器:
java
// GenericApplicationListenerAdapter.java:判断监听器是否支持当前事件
public boolean supportsEventType(ResolvableType eventType) {
// 核心逻辑:监听器声明的事件类型 和 当前事件类型 匹配则返回true
return this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType);
}
✅ 完整流程:
environmentPrepared节点 → EventPublishingRunListener创建环境事件 → 广播器筛选监听器 → 执行感兴趣监听器的逻辑。
六、SpringBoot 启动全生命周期事件(必背)
SpringBoot 为启动流程定义了7个标准事件,按执行顺序排列:
| 事件类名 | 触发时机 | 核心作用 |
|---|---|---|
ApplicationStartingEvent |
启动最开始,环境/容器创建前 | 启动初始化 |
ApplicationEnvironmentPreparedEvent |
环境创建完成,容器刷新前 | 日志初始化、配置加载 |
ApplicationContextInitializedEvent |
容器初始化完成,Bean加载前 | 容器前置处理 |
ApplicationPreparedEvent |
容器刷新完成,Bean初始化前 | 容器准备完成 |
ApplicationStartedEvent |
Bean初始化完成,Runner执行前 | 应用已启动 |
ApplicationReadyEvent |
Runner执行完成 | 应用就绪可访问 |
ApplicationFailedEvent |
启动失败 | 启动异常处理 |
七、实战:自定义监听器(两种方式)
讲完原理,我们动手写一个监听应用启动完成事件的自定义监听器,验证原理。
方式1:实现 ApplicationListener 接口(最通用)
java
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* 自定义监听器:监听应用就绪事件
*/
@Component
public class MyAppReadyListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 事件触发时执行的业务逻辑
System.out.println("==============================");
System.out.println("SpringBoot应用启动完成!可以访问了");
System.out.println("应用名称:" + event.getSpringApplication().getAllSources().toString());
System.out.println("==============================");
}
}
方式2:通过 spring.factories 配置(无@Component场景)
- 去掉
@Component注解; - 在
resources/META-INF/spring.factories中配置:
properties
org.springframework.context.ApplicationListener=\
com.example.demo.MyAppReadyListener
测试结果
启动SpringBoot应用,控制台会打印:
==============================
SpringBoot应用启动完成!可以访问了
应用名称:[com.example.demo.SpringBootDemoApplication]
==============================
八、源码全流程流程图(一张图吃透)
SpringApplication.run
加载监听器
spring.factories
运行监听器:EventPublishingRunListener
应用监听器:内置+自定义
注册到SpringApplicationRunListeners
注册到SpringApplication成员变量
EventPublishingRunListener构造
绑定所有应用监听器到广播器
启动生命周期节点触发
environmentPrepared/starting等
创建对应Spring事件
事件广播器SimpleApplicationEventMulticaster
筛选感兴趣的监听器
执行监听器onApplicationEvent方法
九、核心总结
- 两类监听器分工 :
SpringApplicationRunListener:监听启动生命周期,仅EventPublishingRunListener一个实现;ApplicationListener:执行业务逻辑,可自定义多个。
- 加载方式 :SPI机制从
spring.factories加载,支持无缝扩展。 - 核心枢纽 :
EventPublishingRunListener绑定运行流程和应用监听器。 - 事件机制:启动节点→创建事件→广播器筛选→监听器执行。
- 扩展方式 :实现
ApplicationListener+指定事件类型,即可自定义监听器。
十、面试高频问题
- SpringBoot监听器和Servlet监听器的区别?
- Servlet监听器:监听Web容器(Tomcat)生命周期;
- SpringBoot监听器:监听Spring应用生命周期,更细粒度。
- 为什么运行监听器只有一个实现?
官方设计EventPublishingRunListener作为统一事件转发器,避免多监听器冲突,简化架构。 - 监听器的执行顺序如何控制?
实现Ordered接口或添加@Order注解,数字越小优先级越高。