当前位置: 首页 > news >正文

驾驭 Spring Boot 事件机制:8 个内置事件 + 自定义扩展实战

驾驭 Spring Boot 事件机制:8 个内置事件 + 自定义扩展实战

在 Spring Boot 应用的完整生命周期中,框架为我们预埋了 8 个关键事件(Application-level & Context-level)。 理解并善用这些事件,可以在“不侵入框架、不修改源码”的前提下,注入个性化初始化、监控、清理逻辑。 本文将带你从 0 到 1 掌握事件机制,并给出可直接落地的代码模板。


一、为什么需要事件机制?

场景传统做法事件机制优势
启动时加载字典缓存CommandLineRunner无侵入、可插拔、可排序
优雅停机@PreDestroy与 Spring 生命周期同步,确保资源释放顺序
多模块解耦直接调用发布-订阅,模块间零依赖

二、Spring Boot 8 大内置事件一览

事件触发阶段典型用途监听器注册方式
ApplicationStartingEventrun() 刚被调用,日志系统尚未初始化极早期检查、初始化日志桥接SpringApplication.addListeners(...)
ApplicationEnvironmentPreparedEventEnvironment 已就绪,但 BeanDefinition 尚未加载动态修改配置源、激活 Profile同上
ApplicationContextInitializedEventApplicationContext 已创建,但尚未 refresh注册 BeanFactoryPostProcessor同上
ApplicationPreparedEventBeanDefinition 已加载,Environment 可用读取配置、校验必备属性同上
ContextRefreshedEventrefresh() 完成,所有单例已实例化缓存预热、注册监控@Component
ServletWebServerInitializedEvent内嵌容器端口已打开获取运行时端口、注册服务发现@Component
ApplicationStartedEvent容器已启动,所有 CommandLineRunner 已执行发送启动成功指标@Component
ApplicationReadyEvent同上,额外保证所有应用初始化器已完成开启流量、发送通知@Component

Spring Boot 2.x 之后新增 ApplicationStartingEventApplicationStartedEvent 等,旧版只有 5 个核心事件。


三、实战:监听 4 个高频事件

1. 启动早期动态注入配置

public class EarlyEnvInjector implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {@Overridepublic void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {ConfigurableEnvironment env = event.getEnvironment();// 模拟从 Apollo/Nacos 拉取最新配置Map<String, Object> override = Map.of("spring.datasource.url", "jdbc:mysql://newHost/dev");env.getPropertySources().addFirst(new MapPropertySource("dynamic", override));}
}

注册方式(在 main 方法里):

SpringApplication app = new SpringApplication(DemoApp.class);
app.addListeners(new EarlyEnvInjector());
app.run(args);

2. 容器刷新后预热缓存

@Component
public class CacheWarmer implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {if (event.getApplicationContext().getParent() == null) { // 防止重复执行DictCache.loadAll();}}
}

3. 优雅停机前释放资源

@Component
public class GracefulShutdown implements ApplicationListener<ContextClosedEvent> {private final ExecutorService pool = Executors.newFixedThreadPool(10);@Overridepublic void onApplicationEvent(ContextClosedEvent event) {pool.shutdown();try {if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {pool.shutdownNow();}} catch (InterruptedException e) {pool.shutdownNow();}}
}

4. 启动完毕发送监控告警

@Component
public class StartupReporter implements ApplicationListener<ApplicationReadyEvent> {@Overridepublic void onApplicationEvent(ApplicationReadyEvent event) {InetAddress host = InetAddress.getLocalHost();String port = event.getApplicationContext().getEnvironment().getProperty("local.server.port");DingTalk.send("✅ 服务启动完成: " + host.getHostAddress() + ":" + port);}
}

四、扩展:自定义业务事件

1. 定义领域事件

public class OrderPaidEvent extends ApplicationEvent {private final Long orderId;private final BigDecimal amount;public OrderPaidEvent(Object source, Long orderId, BigDecimal amount) {super(source);this.orderId = orderId;this.amount = amount;}// getters ...
}

2. 发布事件

@Service
@RequiredArgsConstructor
public class OrderService {private final ApplicationEventPublisher publisher;public void pay(Long orderId) {// 业务逻辑...publisher.publishEvent(new OrderPaidEvent(this, orderId, BigDecimal.valueOf(99)));}
}

3. 多监听器异步消费

@Component
public class InvoiceGenerator {@EventListener@Async("invoiceTaskExecutor")   // 线程池隔离public void onOrderPaid(OrderPaidEvent event) {// 生成电子发票...}
}

五、最佳实践清单

  1. 顺序控制:使用 @Order 或实现 Ordered 接口。
  2. 线程安全:早期事件(如 ApplicationStartingEvent)发布时,Bean 尚未实例化,此时注册逻辑需避免依赖 IOC 容器。
  3. 条件化监听@ConditionalOnPropertyEnvironment 判断,避免在测试环境触发线上逻辑。
  4. 异步场景@Async + 自定义线程池,防止阻塞主流程。
  5. 可观测性:通过 Micrometer 记录事件处理耗时,及时发现慢监听器。

六、小结

目标推荐事件
动态修改配置ApplicationEnvironmentPreparedEvent
容器初始化后一次性任务ContextRefreshedEvent
优雅停机ContextClosedEvent
服务启动成功通知ApplicationReadyEvent
业务解耦自定义 ApplicationEvent
http://www.lryc.cn/news/593203.html

相关文章:

  • 《一行注解解决重复提交:Spring Boot 接口幂等实战》
  • 深入理解设计模式:策略模式的艺术与实践
  • 在非Spring Boot的Spring项目中使用Lock4j
  • 用graphviz画一个关系图
  • 云服务器磁盘IO性能优化的测试与配置方法
  • 2025年7月19日,二维矩阵
  • 智能制造——解读39页汽车行业数字化工厂解决方案【附全文阅读】
  • 异世界历险之数据结构世界(二叉树-leetcode)
  • 国产电科金仓数据库:融合进化,智领未来
  • 【Unity3D实例-功能-移动】角色移动-通过WSAD(Rigidbody方式)
  • 架构探索笔记【1】
  • JavaScript空值安全深度指南
  • windows内核研究(驱动开发之内核编程)
  • Java无服务架构新范式:Spring Native与AWS Lambda冷启动深度优化
  • 【小沐学GIS】基于Rust绘制三维数字地球Earth(Rust、OpenGL、GIS)
  • C++STL系列之概述
  • OpenCV 官翻5 - 机器学习
  • 【web安全】万能密码
  • 物联网系统中的可视化大屏定义
  • UGUI 性能优化系列:第三篇——渲染与像素填充率优化
  • 小明记账簿焕新记:从单色到多彩的主题进化之路
  • 【Android】ListView与RecyclerView的基础使用
  • 安全隔离新选择:SiLM5768L系列 - 集成互锁功能的高速六通道数字隔离器
  • 从随机数值到特征检测器的学习与更新
  • 【Linux驱动-快速回顾】简单了解一下PinCtrl子系统:设备树如何被接解析与匹配
  • 大模型 Function Call 的实现步骤及示例详解
  • SpringBoot 3.0 挥别 spring.factories,拥抱云原生新纪元
  • Java机考题:815. 公交路线 图论BFS
  • 猎板:在 5G 与 AI 时代,印制线路板如何满足高性能需求
  • SQL Server和PostgreSQL填充因子