Spring Boot 启动原理全解析(二)启动监听器原理

在SpringBoot启动过程中,监听器(Listener) 是贯穿始终的核心组件,它负责监听启动生命周期中的关键节点(环境准备、容器刷新、启动完成等),并触发对应事件完成日志初始化、配置加载、缓存清理等核心工作。

很多新手看源码时会困惑:监听器从哪来?存在哪?什么时候被触发?事件怎么传递?本文结合SpringBoot 2.7.18 官方源码 ,从监听器加载→注册→事件产生→事件发布→事件监听全链路拆解,搭配实战案例,用最通俗的语言讲透底层原理。

一、先搞懂核心概念:两类监听器+事件机制

在开始源码分析前,先区分两个核心概念,避免混淆:

1. 两类核心监听器(关键!)

SpringBoot启动过程中,有两个层级的监听器,职责完全不同:

监听器类型 全限定类名 核心作用 数量
运行监听器 SpringApplicationRunListener 监听SpringBoot应用启动的整个生命周期,是启动流程的「总调度员」 固定1个:EventPublishingRunListener
应用监听器 ApplicationListener 监听具体的Spring事件,是实际执行业务逻辑的「干活者」 多个(Spring内置+自定义)

2. 核心工作流程(一句话总结)

  1. SpringBoot启动时,从spring.factories加载运行监听器+应用监听器
  2. 运行监听器EventPublishingRunListener持有所有应用监听器;
  3. 启动到关键节点时,运行监听器产生对应事件
  4. 运行监听器通过事件广播器发布事件;
  5. 对事件感兴趣的应用监听器执行自己的业务逻辑。

二、第一步:监听器从哪来?------ 加载流程(spring.factories)

SpringBoot不会硬编码监听器 ,而是通过SPI机制META-INF/spring.factories文件中加载所有监听器,这是SpringBoot的核心扩展规范。

1. 源码位置

我们看源码中getRunListenersSpringApplication构造方法:

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加载的核心:

  1. 读取所有jar包下的spring.factories
  2. 根据接口全类名,找到对应的实现类全类名;
  3. 通过反射实例化对象;
  4. 排序后返回集合。

结论

  • 运行监听器:仅加载EventPublishingRunListener
  • 应用监听器:加载spring.factories中配置的所有ApplicationListener实现类。

三、第二步:监听器存在哪?------ 注册流程

加载完成后,监听器会被注册到对应容器中,我们分两类看注册位置:

1. 应用监听器:注册到 SpringApplication 成员变量

源码中setListeners方法,将加载的所有ApplicationListener存入SpringApplicationlisteners集合:

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;
}

✅ 存储位置:SpringApplicationRunListenerslisteners集合。


四、第三步:核心桥梁!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 = 启动生命周期监听 + 事件广播转发

  1. 监听SpringBoot启动的每一步;
  2. 持有所有应用监听器;
  3. 启动节点触发时,发布事件给应用监听器。

五、第四步:事件产生与发布 ------ 以环境准备为例

我们以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场景)

  1. 去掉@Component注解;
  2. 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方法


九、核心总结

  1. 两类监听器分工
    • SpringApplicationRunListener:监听启动生命周期,仅EventPublishingRunListener一个实现;
    • ApplicationListener:执行业务逻辑,可自定义多个。
  2. 加载方式 :SPI机制从spring.factories加载,支持无缝扩展。
  3. 核心枢纽EventPublishingRunListener绑定运行流程和应用监听器。
  4. 事件机制:启动节点→创建事件→广播器筛选→监听器执行。
  5. 扩展方式 :实现ApplicationListener+指定事件类型,即可自定义监听器。

十、面试高频问题

  1. SpringBoot监听器和Servlet监听器的区别?
    • Servlet监听器:监听Web容器(Tomcat)生命周期;
    • SpringBoot监听器:监听Spring应用生命周期,更细粒度。
  2. 为什么运行监听器只有一个实现?
    官方设计EventPublishingRunListener作为统一事件转发器,避免多监听器冲突,简化架构。
  3. 监听器的执行顺序如何控制?
    实现Ordered接口或添加@Order注解,数字越小优先级越高。

相关推荐
小松加哲3 小时前
Spring Boot 启动原理全解析(三)加载参数原理
参数设置·spring boot启动原理·运行环境准备
小松加哲4 小时前
Spring Boot 启动原理全解析(一)
spring boot启动原理
雪碧聊技术2 个月前
Oracle数据库、监听器的启动、关闭流程
监听器·oracle数据库·启停流程·相关命令·用户切换
予安灵1 年前
Vue.js 的计算属性和侦听器:提升数据处理与交互的关键工具
前端·javascript·vue.js·前端框架·计算属性·监听器
阿小木的愤怒1 年前
Spring监听器Listener
java·spring·监听器·spring监听器
WenGyyyL2 年前
Java Web 开发 - 掌握拦截器和监听器
java·开发语言·前端·拦截器·监听器
Davieyang.D.Y2 年前
互联网轻量级框架整合之JavaEE基础II
java-ee·过滤器·监听器·servlet初始化
金刚猿3 年前
207、SpringBoot 整合 RabbitMQ 实现消息的发送 与 接收(监听器)
spring boot·java-rabbitmq·监听器