ApplicationListener 和 SpringApplicationRunListener 深度解析对比

在 Spring Boot 中,ApplicationListener 和 SpringApplicationRunListener 是两个作用层级、加载时机和使用场景都截然不同的监听器。

它们最核心的区别在于:‌

ApplicationListener 是 Spring 容器内的"业务事件监听器",

SpringApplicationRunListener 是 Spring Boot 启动流程的"生命周期监听器"。

核心区别概述

这两个接口服务于不同的阶段和目的:

  • ApplicationListener :关注的是 Spring 容器内部 发生的事件,主要用于业务逻辑 或通用服务在容器生命周期不同阶段(包括应用启动阶段,跟事件监听类型有关)的操作。
  • SpringApplicationRunListener :关注的是 Spring Boot 应用启动过程本身 的关键节点,主要用于框架层面 或基础设施 在容器初始化之前 或期间的操作。

详细对比

  1. 所属框架与作用域

    • ApplicationListener :
      • 属于 Spring Framework。
      • 作用域是整个 Spring 应用上下文(ApplicationContext)的生命周期。容器启动后,它可以监听容器内发布的各种事件。
    • SpringApplicationRunListener :
      • 属于 Spring Boot。
      • 作用域仅限于 SpringApplication.run(...) 方法的执行过程。它监听的是应用启动流程中的特定阶段。
  2. 加载时机与管理方式

    • ApplicationListener :
      • 作为 Spring Bean 被管理。
      • 加载时机:在 Spring 容器启动过程中,随着其他 Bean 一起被创建和初始化。
      • 注册方式:可以通过 @Component、@Bean 注解定义,或者通过 SpringApplication.addListeners(...) 方法添加。
      • 支持依赖注入 :可以像普通 Bean 一样使用 @Autowired、@Value 等。
    • SpringApplicationRunListener :
      • 不由 Spring 容器管理。
      • 加载时机:非常早 ,在 Spring 容器创建之前。
      • 注册方式:必须通过在 META-INF/spring.factories 文件中配置 org.springframework.boot.SpringApplicationRunListener 键来声明实现类的全限定名(使用 SPI 机制)。
      • 不支持依赖注入 :因为它加载时 Spring 容器还不存在,无法注入其他 Bean 或读取 @Value 配置。只能通过构造参数(接收 SpringApplication 实例和 String[] args)或直接访问环境等方式获取信息。
  3. 监听的事件类型

    • ApplicationListener:

      • 监听 ApplicationEvent 及其子类。
      • 事件来源广泛:
        • Spring 内置事件 :如 ContextRefreshedEvent(容器刷新完成)、ContextClosedEvent(容器关闭)。
        • Spring Boot 启动事件 :如 ApplicationStartedEvent、ApplicationReadyEvent(注意:这些事件通常是由 EventPublishingRunListener 转发的 SpringApplicationRunListener 事件,即可以监听spring boot 的启动生命周期,使用形式ApplicationListener<ApplicationStartedEvent>)。
        • 自定义业务事件 :开发者可以定义自己的事件类(如 OrderPaidEvent)并发布。
        • 实现方式:使用@EventListener注解简化监听,或者实现 ApplicationListener<自定义事件>,在 onApplicationEvent 中编写处理逻辑。
    • SpringApplicationRunListener:

      • 监听的是 Spring Boot 启动流程中的固定阶段 ,每个阶段对应接口中的一个方法回调:
        • starting:run() 方法刚开始执行。
        • environmentPrepared:Environment 准备好,但 ApplicationContext 还未创建。
        • contextPrepared:ApplicationContext 创建好,但在加载 Bean 定义之前。
        • contextLoaded:ApplicationContext 加载完 Bean 定义,但在刷新容器之前。
        • started:ApplicationContext 已刷新,应用已启动,但 CommandLineRunner/ApplicationRunner 还未执行。
        • running:所有 Runner 已执行完毕,run() 方法即将结束。
        • failed:启动过程中发生异常。
  4. 典型使用场景

    • ApplicationListener :
      • 业务逻辑解耦 :监听自定义业务事件(如 UserRegisteredEvent),触发发送邮件、初始化用户信息等操作。
      • 容器生命周期钩子 :监听 ContextRefreshedEvent 进行缓存预热、初始化全局服务;监听 ApplicationReadyEvent 执行最终检查或通知外部系统应用已就绪。
      • 响应内置事件 :监听 ContextClosedEvent 执行资源清理。
    • SpringApplicationRunListener :
      • 极早期配置处理 :在 environmentPrepared 阶段从远程配置中心(如 Nacos, Consul)拉取配置,或者对配置文件中的加密属性进行解密。
      • 启动过程定制 :在 starting 阶段输出自定义 Banner 或启动日志。
      • 环境检查与准备 :在 environmentPrepared 阶段验证环境变量或进行必要的系统设置。
      • 框架/工具集成 :在容器初始化前(如 contextPrepared 或更早)集成 APM 监控探针进行字节码增强。

两者联系

Spring Boot 提供了一个关键的 SpringApplicationRunListener 实现:EventPublishingRunListener。它的作用是将 SpringApplicationRunListener 接收到的各个阶段回调(如 starting, environmentPrepared)转换并发布 成对应的 ApplicationEvent (如 ApplicationStartingEvent, ApplicationEnvironmentPreparedEvent)。这些事件随后会被广播给所有注册的 ApplicationListener。

因此,ApplicationListener 可以间接感知到 Spring Boot 的启动过程(通过监听 ApplicationStartingEvent, ApplicationEnvironmentPreparedEvent 等事件),而 SpringApplicationRunListener 提供了更底层、更早期的介入点。EventPublishingRunListener 充当了桥梁角色。

总结对比表

特性 ApplicationListener SpringApplicationRunListener
所属框架 Spring Framework Spring Boot
作用域 Spring 容器生命周期 SpringApplication.run() 执行过程
管理方式 Spring 容器管理的 Bean SPI 加载,不受容器管理
依赖注入支持 支持 (@Autowired, @Value) 不支持
注册方式 @Component, @Bean, addListeners() META-INF/spring.factories (SPI)
事件/阶段 ApplicationEvent 及其子类 启动流程的固定方法回调 (starting 等)
典型场景 业务解耦、缓存预热、处理自定义事件 启动前配置加载、解密、环境检查、APM 集成
执行时机 (相对容器) 容器初始化后 容器初始化前 或期间

理解这些区别有助于在合适的场景选择正确的扩展点。

相关推荐
Wang's Blog5 小时前
Java 项目实战: 交友项目 - 完善用户信息章节复盘:套路清单与自检清单
java·dubbo·交友
用户3126874877205 小时前
Java 11-21 新特性盘点:var、record、sealed、虚拟线程
java
TangGeeA5 小时前
命令安全设置
java·服务器·安全
欣栀♡5 小时前
Spring 循环依赖不止三级缓存,Boot 2.6 起这个开关默认已经关了
java·spring·缓存
SL_staff5 小时前
3分钟上线新规则:业务人员拖拽完成合同超期自动暂停服务
java·mysql·全栈
星夜离尘565 小时前
MATLAB阶跃响应怎么量:别把最后一个采样点当成稳态
开发语言·matlab
Android小码家5 小时前
Vehicle 实战 自定义属性+测试demo
java·c++·framework·vhal·vehicle
范同学~5 小时前
固定表头+第一列+最后一列
java·服务器·前端
云水初6 小时前
【agent篇】RAG Agent避坑指南(附高级RAG Agent汇总)
开发语言·笔记·python·学习·langchain·agent·rag
Littlehero_1216 小时前
QT自定义控件之水泥厂组态(源码开源)
开发语言·qt