← 返回题目列表

Spring Boot 启动过程中会发布哪些事件?怎么监听这些启动事件?

中等 第 22 / 25 题 更新于 2026/07/28
启动事件SpringApplicationEvent事件监听ApplicationReadyEvent

简化版

**Spring Boot 在启动过程中会按顺序发布一系列「应用事件」,你可以监听它们,在启动的特定阶段插入自己的逻辑。**主要的启动事件(按发布顺序):ApplicationStartingEvent——刚开始启动、几乎什么都还没做(连 Environment 都没准备好);ApplicationEnvironmentPreparedEvent——Environment 准备好了(配置、profile 已加载),但容器还没创建;ApplicationContextInitializedEvent——容器创建好、但 Bean 定义还没加载;ApplicationPreparedEvent——Bean 定义加载完、但还没 refresh(Bean 还没实例化);ApplicationStartedEvent——容器 refresh 完成、Bean 都就绪,但 Runner 还没执行;ApplicationReadyEvent——应用完全就绪(Runner 执行完,可对外服务,最常用);ApplicationFailedEvent——启动过程中出错时发布。怎么监听:常规的 @EventListener 只能监听 ApplicationReadyEvent 之后的(因为它依赖容器就绪);更早的事件(如 ApplicationStartingEvent)要用 SpringApplication.addListeners() 或在 META-INF/spring.factories 里注册 ApplicationListener(因为那时容器还没起来,@EventListener 还不可用)。

详细版

启动事件顺序与时机

顺序事件时机Environment容器Bean
1ApplicationStartingEvent刚开始
2ApplicationEnvironmentPreparedEvent环境就绪
3ApplicationContextInitializedEvent容器已建✅(空)
4ApplicationPreparedEventBean 定义已载定义✅ 实例❌
5ApplicationStartedEventrefresh 完
6ApplicationReadyEvent完全就绪✅ + Runner✅
ApplicationFailedEvent启动失败
// 方式一:监听 ApplicationReadyEvent(容器已就绪,@EventListener 可用)
@Component
public class ReadyListener {
    @EventListener(ApplicationReadyEvent.class)
    public void onReady(ApplicationReadyEvent event) {
        // 应用完全就绪后执行(最常用的启动后钩子)
    }
}

// 方式二:监听更早的事件——用 SpringApplication.addListeners
public static void main(String[] args) {
    SpringApplication app = new SpringApplication(App.class);
    app.addListeners((ApplicationListener<ApplicationStartingEvent>) e -> {
        // 极早期,容器还没起来
    });
    app.run(args);
}
// 方式三:META-INF/spring.factories 注册 ApplicationListener(同样能监听早期事件)

⚠️ 关键认知:越早的事件,能用的东西越少,监听方式也受限@EventListener 是靠 Spring 容器的事件机制工作的——它要先有一个就绪的容器来处理 @EventListener 注解。所以 @EventListener 只能监听容器就绪之后的事件ApplicationStartedEventApplicationReadyEvent);而 ApplicationStartingEventApplicationEnvironmentPreparedEvent 这些发生在「容器还没起来」时,@EventListener 根本还没生效,必须用 SpringApplication.addListeners()spring.factories 注册 ApplicationListener(这些是 Spring Boot 用另一套机制在早期就加载的)。这也解释了为什么日志系统、配置加载这类「极早期就要工作」的组件,都是通过 spring.factories 注册监听器来介入的。

完整版教学

一、为什么要有启动事件

Spring Boot 启动事件的价值是「在启动的不同阶段提供介入点」:

启动是一个"分阶段"的过程:
  准备环境 → 创建容器 → 加载 Bean 定义 → 实例化 Bean → 就绪

不同的组件需要在"不同阶段"介入:
  - 日志系统:要在"环境准备好"时就初始化(拿到日志配置)
  - 配置加密/外部配置:要在"环境准备阶段"介入
  - 一些框架:要在"Bean 定义加载后、实例化前"动手脚
  - 业务初始化:要在"完全就绪后"执行

Spring Boot 的方案:在每个关键阶段发布一个事件
  想在某阶段介入 → 监听对应的事件
  → 把"启动流程"变成"可插拔的、事件驱动的"扩展点

启动事件的价值是「把启动流程的各阶段变成可介入的扩展点」——启动是分阶段的(准备环境→建容器→加载定义→实例化→就绪),不同组件要在不同阶段介入(日志系统在环境就绪时、业务初始化在完全就绪后)。Spring Boot 在每个关键阶段发布一个事件,监听对应事件就能在该阶段插入逻辑。理解「启动分阶段、不同组件在不同阶段介入、Spring Boot 每阶段发事件让启动流程可插拔」,就理解了启动事件的设计意图。

二、七个事件的顺序与时机

把七个事件按顺序和「此时什么就绪了」讲清楚:

① ApplicationStartingEvent:
   刚调 run()、几乎什么都没做(Environment、容器都还没有)
   用途:极早期初始化(如日志系统的最早介入点)

② ApplicationEnvironmentPreparedEvent:
   Environment 准备好了(配置文件、profile、环境变量都加载了)
   但容器还没创建
   用途:读/改配置、根据配置做早期决策(如根据 profile 决定日志配置)

③ ApplicationContextInitializedEvent:
   ApplicationContext 创建好了,但 Bean 定义还没加载、Bean 还没实例化
   用途:对容器做早期定制(ApplicationContextInitializer 相关)

④ ApplicationPreparedEvent:
   Bean 定义已加载(BeanDefinition 都注册了),但还没 refresh(没实例化)
   用途:在实例化前操作 Bean 定义

⑤ ApplicationStartedEvent:
   容器 refresh 完成,所有 Bean 都实例化、初始化好了
   但 CommandLineRunner/ApplicationRunner 还没执行
   用途:Bean 就绪后、Runner 前的介入

⑥ ApplicationReadyEvent:★ 最常用
   Runner 也执行完了,应用完全就绪、可对外服务
   用途:启动后业务初始化、"应用已就绪"的标志(发通知、注册服务发现)

⑦ ApplicationFailedEvent:
   启动过程中任何阶段抛异常 → 发布它
   用途:启动失败的兜底处理(告警、清理)

七个事件按启动进度排列,核心是「每个事件时刻什么就绪了」:Starting(啥都没有)→ EnvironmentPrepared(配置就绪、容器没有)→ ContextInitialized(容器建好、Bean 定义没有)→ Prepared(Bean 定义就绪、没实例化)→ Started(Bean 就绪、Runner 没执行)→ Ready(完全就绪,最常用)→ Failed(出错时)。理解每个事件对应的就绪状态,就知道该监听哪个。理解「七事件按启动进度:Starting→EnvironmentPrepared→ContextInitialized→Prepared→Started→Ready(最常用)→Failed、每个对应不同就绪状态」,就掌握了事件顺序。

三、@EventListener 的限制:容器就绪后才可用

监听事件有个关键限制——@EventListener 只能用于「容器就绪后」的事件:

@EventListener 是怎么工作的?
  它靠 Spring 容器的事件机制——需要容器扫描到这个注解、
  注册成事件监听器;这要求"容器已经就绪、能处理注解"

所以 @EventListener 只能监听:
  ⑤ ApplicationStartedEvent(容器 refresh 完,@EventListener 已生效)
  ⑥ ApplicationReadyEvent
  → 这两个是容器就绪之后的

不能用 @EventListener 监听(因为那时容器还没起来):
  ① ApplicationStartingEvent
  ② ApplicationEnvironmentPreparedEvent
  ③ ApplicationContextInitializedEvent
  ④ ApplicationPreparedEvent(这个略特殊,容器有了但 @EventListener 未必可靠)
  → 这些"早期事件"要用别的方式监听(下一节)

记忆:@EventListener 只覆盖"容器就绪后",早期事件够不着

@EventListener 的限制是「只能监听容器就绪后的事件」——它靠 Spring 容器的事件机制工作(要容器扫描注解、注册监听器),所以只覆盖 ApplicationStartedEventApplicationReadyEvent 这些「容器 refresh 完成后」的事件。而 ApplicationStartingEvent 等早期事件发生在容器还没起来时,@EventListener 够不着。理解「@EventListener 靠容器事件机制、只能监听容器就绪后的事件(Started/Ready)、早期事件够不着」,就理解了监听方式的核心限制。

四、监听早期事件:addListeners 与 spring.factories

监听早期事件要用「不依赖容器」的两种方式:

早期事件(容器没起来时发布的)要用这两种方式监听:

方式一:SpringApplication.addListeners()
  public static void main(String[] args) {
      SpringApplication app = new SpringApplication(App.class);
      app.addListeners((ApplicationListener<ApplicationStartingEvent>) e -> {
          // 在启动最早期执行
      });
      app.run(args);
  }
  → 在 run() 之前手动注册,能监听所有事件(含最早的)

方式二:META-INF/spring.factories 注册 ApplicationListener
  org.springframework.context.ApplicationListener=\
    com.example.MyStartingListener
  → Spring Boot 启动极早期就会加载 spring.factories 里的监听器
  → 所以能监听最早的事件(框架/库常用这种方式介入)

为什么这两种能监听早期事件:
  它们不依赖"容器就绪"——是 Spring Boot 在启动流程里
  用 SpringApplicationRunListeners 机制主动加载和调用的
  → 在容器起来之前就能工作

Spring Boot 3.x 也支持在 spring.factories 之外用
  spring-boot 的新机制注册,但思路相同

监听早期事件用不依赖容器的两种方式:SpringApplication.addListeners()(在 run() 前手动注册,能监听所有事件);META-INF/spring.factories 注册 ApplicationListener(Spring Boot 启动极早期就加载,框架/库常用)。它们不依赖「容器就绪」——是 Spring Boot 用 SpringApplicationRunListeners 机制主动加载调用的,在容器起来前就能工作。理解「监听早期事件用 addListeners 或 spring.factories 注册 ApplicationListener、不依赖容器就绪、Spring Boot 主动加载调用」,就掌握了监听早期事件的方式。

五、SpringApplicationRunListener:事件的发布者

深入一层:这些事件是谁发布的?是 SpringApplicationRunListener

SpringApplicationRunListener:
  Spring Boot 启动过程的"广播员"
  在 run() 的各个阶段,调用它的方法来发布对应事件:
    starting()          → 发 ApplicationStartingEvent
    environmentPrepared → 发 ApplicationEnvironmentPreparedEvent
    contextPrepared/Loaded → ...
    started()           → 发 ApplicationStartedEvent
    ready()             → 发 ApplicationReadyEvent
    failed()            → 发 ApplicationFailedEvent

默认实现:EventPublishingRunListener
  它内部有一个事件广播器(早期用 SimpleApplicationEventMulticaster)
  把这些事件广播给所有注册的 ApplicationListener

所以整条链路:
  SpringApplication.run() 各阶段
    → 调 SpringApplicationRunListener 的对应方法
    → EventPublishingRunListener 广播事件
    → 各 ApplicationListener 收到并处理

事件的发布者是 SpringApplicationRunListener(Spring Boot 启动的「广播员」)——run() 的各阶段调它的方法(starting/environmentPrepared/started/ready/failed)发布对应事件,默认实现 EventPublishingRunListener 内部用事件广播器把事件广播给所有 ApplicationListener。这解释了「为什么早期事件能被 spring.factories 注册的监听器收到」——因为 SpringApplicationRunListener 在容器起来前就已经在广播了。理解「SpringApplicationRunListener 是启动广播员、各阶段发对应事件、EventPublishingRunListener 广播给 ApplicationListener」,就理解了启动事件的发布机制。

六、实际应用与选择

总结启动事件的实际用途和「该监听哪个」:

常见需求 → 该监听哪个事件:
  启动后执行业务初始化(预热、通知、注册服务发现)
    → ApplicationReadyEvent(最常用,应用完全就绪)
    → 或直接用 CommandLineRunner/ApplicationRunner(时机接近)

  需要在配置加载后、容器创建前做事(如根据配置定制)
    → ApplicationEnvironmentPreparedEvent(用 addListeners/spring.factories)

  启动失败时告警/清理
    → ApplicationFailedEvent

  框架/库要在极早期介入
    → ApplicationStartingEvent(spring.factories 注册)

ApplicationReadyEvent vs CommandLineRunner:
  两者时机接近(都在应用就绪后)
  - Runner:更直接,适合"执行一段初始化代码"
  - ReadyEvent:事件语义更明确,且能被多个监听器解耦地处理
  - 顺序:Runner 先执行,然后才发 ApplicationReadyEvent

选择原则:
  能用 @EventListener(ApplicationReadyEvent) 就用它(简单)
  要监听早期事件才用 addListeners / spring.factories

实际选择「监听哪个事件」按需求定:启动后业务初始化 → ApplicationReadyEvent(最常用)或 Runner配置加载后容器前 → ApplicationEnvironmentPreparedEvent(早期,用 addListeners)启动失败告警 → ApplicationFailedEvent框架极早期介入 → ApplicationStartingEvent(spring.factories)ApplicationReadyEvent 和 Runner 时机接近(Runner 先执行、再发 ReadyEvent),能用简单的 @EventListener(ApplicationReadyEvent) 就用它。理解「按需求选事件:业务初始化用 ReadyEvent(最常用)、早期定制用 EnvironmentPrepared、失败用 Failed;Runner 先于 ReadyEvent;能用 @EventListener 就用」,就掌握了启动事件的实际应用。

记忆钩子:「Spring Boot 启动事件(按顺序):①ApplicationStartingEvent(啥都没有)②EnvironmentPreparedEvent(配置就绪容器没有)③ContextInitializedEvent(容器建好Bean定义没有)④PreparedEvent(Bean定义就绪没实例化)⑤StartedEvent(Bean就绪Runner没执行)⑥ReadyEvent(完全就绪,最常用)⑦FailedEvent(出错);@EventListener 只能监听容器就绪后的(Started/Ready);早期事件用 SpringApplication.addListeners 或 spring.factories 注册 ApplicationListener(不依赖容器);发布者是 SpringApplicationRunListener(EventPublishingRunListener 广播);Runner 先于 ReadyEvent」

七、常见误区与追问

  • 误区:所有启动事件都能用 @EventListener 监听。 不能——@EventListener 靠容器事件机制,只能监听容器就绪后的事件(ApplicationStartedEvent、ApplicationReadyEvent);早期事件(ApplicationStartingEvent 等)容器还没起来,要用 SpringApplication.addListeners() 或 spring.factories 注册 ApplicationListener。
  • 误区:ApplicationStartedEvent 就是应用完全就绪了。 ApplicationStartedEvent 时容器 refresh 完、Bean 就绪,但 CommandLineRunner/ApplicationRunner 还没执行;ApplicationReadyEvent 才是「Runner 也跑完、应用完全就绪」——两者差一个 Runner 执行阶段。
  • 误区:越早的事件能用的资源越多。 恰恰相反——越早的事件能用的越少:ApplicationStartingEvent 时连 Environment 都没有;EnvironmentPreparedEvent 才有配置;Started/Ready 才有就绪的 Bean;监听早期事件时不能指望注入 Bean。
  • 误区:启动失败没有事件通知。 有 ApplicationFailedEvent——启动过程任何阶段抛异常都会发布它,可以监听它做告警、清理、记录失败原因(配合 FailureAnalyzer 分析失败)。
  • 追问:ApplicationReadyEvent 和 CommandLineRunner 有什么区别,谁先谁后? 时机接近(都在应用就绪后),Runner 先执行、执行完才发 ApplicationReadyEvent;Runner 更直接(写一段初始化代码),ReadyEvent 语义更明确且能被多个监听器解耦处理;一般用哪个都行,能用 @EventListener(ApplicationReadyEvent) 就用它。
  • 追问:日志系统为什么能在极早期就初始化? Spring Boot 通过 spring.factories 注册了监听 ApplicationStartingEvent/ApplicationEnvironmentPreparedEvent 的监听器(如 LoggingApplicationListener),这些监听器不依赖容器,在启动极早期就被 SpringApplicationRunListener 广播到,从而能在环境准备阶段就初始化日志系统。
  • 追问:这些事件是谁发布的? SpringApplicationRunListener(默认实现 EventPublishingRunListener)——SpringApplication.run() 在各阶段调用它的 starting/environmentPrepared/started/ready/failed 等方法,由它内部的事件广播器把事件广播给所有注册的 ApplicationListener。

八、加强记忆

Spring Boot 启动时按顺序发布一系列「应用事件」,监听它们可在启动特定阶段插入逻辑。七个事件(按发布顺序,核心是「此时什么就绪了」):ApplicationStartingEvent(刚开始,Environment/容器都没有)→ ApplicationEnvironmentPreparedEvent(配置/profile 就绪、容器没有)→ ApplicationContextInitializedEvent(容器建好、Bean 定义没有)→ ApplicationPreparedEvent(Bean 定义就绪、没实例化)→ ApplicationStartedEvent(refresh 完、Bean 就绪、Runner 没执行)→ ApplicationReadyEvent(Runner 也跑完、完全就绪,最常用)→ ApplicationFailedEvent(启动出错时)。监听方式@EventListener 靠容器事件机制,只能监听容器就绪后的事件Started/Ready);早期事件Starting/EnvironmentPrepared)容器还没起来,要用 SpringApplication.addListeners()META-INF/spring.factories 注册 ApplicationListener(不依赖容器,Spring Boot 启动极早期就加载)。事件的发布者是 SpringApplicationRunListener(默认 EventPublishingRunListener 广播)。业务初始化用 ApplicationReadyEvent 或 Runner(Runner 先于 ReadyEvent)。一句话「Spring Boot 启动事件按序:Starting→EnvironmentPrepared→ContextInitialized→Prepared→Started→Ready(最常用)→Failed;@EventListener 只能监听容器就绪后的(Started/Ready),早期事件用 addListeners/spring.factories 注册 ApplicationListener;发布者 SpringApplicationRunListener;Runner 先于 ReadyEvent」。