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 启动事件 :如 ApplicationStartedEventApplicationReadyEvent(注意:这些事件通常是由 EventPublishingRunListener 转发的 SpringApplicationRunListener 事件,即可以监听spring boot 的启动生命周期,使用形式ApplicationListener<ApplicationStartedEvent>)。
        • 自定义业务事件 :开发者可以定义自己的事件类(如 OrderPaidEvent)并发布。
        • 实现方式:使用@EventListener注解简化监听,或者实现 ApplicationListener<自定义事件>,在 onApplicationEvent 中编写处理逻辑。
    • SpringApplicationRunListener

      • 监听的是 Spring Boot 启动流程中的固定阶段 ,每个阶段对应接口中的一个方法回调:
        • startingrun() 方法刚开始执行。
        • environmentPreparedEnvironment 准备好,但 ApplicationContext 还未创建。
        • contextPreparedApplicationContext 创建好,但在加载 Bean 定义之前。
        • contextLoadedApplicationContext 加载完 Bean 定义,但在刷新容器之前。
        • startedApplicationContext 已刷新,应用已启动,但 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 集成
执行时机 (相对容器) 容器初始化 容器初始化期间

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

相关推荐
quan26314 分钟前
20260529,日常开发-数据库主从问题
java·mysql·主从·延迟
JacksonMx22 分钟前
@Transactional 最佳实践
java·spring boot·spring·性能优化
Sincerelyplz29 分钟前
【AI会议纪要实践】mapReduce、RAG 与结构化输出
java·后端·agent
过期动态35 分钟前
【LeetCode 热题 100】接雨水
java·数据结构·算法·leetcode·职场和发展
bug和崩溃我都要43 分钟前
Qt 封装 libmpv 全功能视频播放器开发指南
开发语言·qt·音视频
郝学胜-神的一滴1 小时前
Qt 高级开发 018:复刻经典登录界面布局与窗口美化全解析
开发语言·c++·qt·程序人生·用户界面
郝亚军1 小时前
IEEE 754 单精度浮点的SEM表示
开发语言·c++·算法
zhangjw341 小时前
第15篇:Java多线程零基础入门,进程线程、线程创建方式、线程生命周期、线程安全彻底吃透
java·开发语言·面试
蝈理塘(/_\)大怨种1 小时前
类和对象 (上)
java·开发语言
小新1101 小时前
qt creator 将qInfo的输出日志写入日志文档,方便查看
开发语言·qt