驾驭 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.pswp.cn/news/915295.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/915295.shtml
英文地址,请注明出处:http://en.pswp.cn/news/915295.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【kafka4源码学习系列】kafka4总体架构介绍

二 kafka架构介绍学习一个系统之前很重要的一点就是先了解这个系统整体的架构&#xff0c;这能够使我们对整个系统有个总体的认识&#xff0c;清楚地知道这个系统有什么能力。这不仅帮助我们学习时快速定位到我们想要的内容&#xff0c;还能避免我们学习过程中在庞大的系统中迷…

java内存图

java内存图java文件运行流程程序的内存空间认识虚拟机栈程序的执行流程认识堆java的类与对象的关系java文件运行流程 有这样的一份 java 文件 在该目录下的终端运行 javac Hello.java 命令&#xff0c;会生成 Hello.class 文件&#xff0c;内容如下&#xff1a; Hello.java 打…

vscode编辑Markdown文件

一.安装Markdown的插件 vscode的扩展&#xff0c;搜索Markdown Preview Enhanced的插件&#xff0c;并安装。 其他的常用插件&#xff0c;还包括&#xff1a; Markdown All in One &#xff1a;提供了许多有用的功能&#xff0c;如快捷键支持、自动预览、TOC&#xff08;目录&…

【PTA数据结构 | C语言版】查找树中带有指定数据的结点

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;创建有 4 个结点的树&#xff0c;然后查找给定的 x。 输入格式&#xff1a; 输入首先在第一行给出 4 个正整数&#xff0c;依次对应树的根结点、根的第 1、2、3 个孩子结点的…

PostgreSQL常用命令与工具指南

文章目录PostgreSQL常用命令与工具指南简介1. 连接与基本操作连接数据库环境变量设置&#xff08;避免密码输入&#xff09;常用元命令2. 数据库与表管理数据库操作创建数据库删除数据库修改数据库属性表操作创建表修改表结构删除表索引管理创建索引删除索引3. 数据操作(CRUD)插…

SpringBoot项目部署至云服务器

目录 一、后端项目部署 1、修改配置文件 2、清理打包缓存&#xff0c;打jar包&#xff08;两种方式二选一&#xff09; 自动打包 手动打包 打包成功状态 3、将jar包导入宿主机上 jar包位置 jar包上传 jar包运行 浏览器测试 二、前端代码 docker搭建nginx的基本步骤 打…

Agent-S:重新定义下一代 AI 智能体开发框架

Agent-S&#xff1a;重新定义下一代 AI 智能体开发框架 —— 探索 simular-ai 的开源革命 引言 2025 年&#xff0c;AI 智能体&#xff08;Agent&#xff09;技术正从概念走向产业核心。从自动化工作流到复杂决策系统&#xff0c;开发者亟需更高效的工具链。在这一背景下&am…

保持视频二维码不变,如何更新视频内容,节省物料印刷成本

保持视频二维码不变&#xff0c;如何更新视频内容&#xff0c;节省物料印刷成本&#xff1f; 视频替换功能&#xff0c;是指在保持视频二维码不变、视频观看地址不变、视频调用代码不变的情况下替换视频内容&#xff0c;从而节省用户印刷物料的成本&#xff0c;满足用户更新视…

flutter项目调试问题小结

背景 目标是用android studio flutter 跑hello world 下载 android studio 我下载的是2024.3.2.15版本 最新版下载首页就能下&#xff1a;下载 Android Studio 和应用工具 - Android 开发者 | Android Developers 历史版本可在归档列表下载&#xff1a;Android Studio…

明细列表,明细grid中的默认按钮失效,配置按钮失效

明细列表&#xff0c;明细grid中的默认按钮失效&#xff0c;配置按钮失效原因&#xff1a;采用通配的写法导致的默认按钮失效if(menuDetails){menuDetails.forEach((item) > {const { name, menu_detail_columns, menu_detail_buttons, save_url} item;this.set(${name}Gri…

Matplotlib 30分钟精通

📊 Matplotlib 30分钟精通计划(完整版含输出) ⏰ 时间分配 5分钟:Matplotlib基础概念和简单图表 10分钟:常用图表类型详解 10分钟:图表美化和定制 5分钟:综合实战练习 📚 第一部分:Matplotlib基础概念 (5分钟) 1. 什么是Matplotlib? import matplotlib.pyplot a…

7月19日 暴雨蓝色预警:全国多地迎强降雨,需防范次生灾害

中央气象台7月19日10时继续发布暴雨蓝色预警,预计未来24小时(19日14时至20日14时),我国多地将迎来大到暴雨,局地甚至出现大暴雨,并伴有短时强降水、雷暴大风等强对流天气,需加强防范。 强降雨覆盖范围广,多地需警惕极端降水 此次降雨影响范围广泛,涉及华北、华南、西…

Redis学习-05Redis基本数据结构

Redis 数据结构 String 字符串 基本命令表命令执行效果时间复杂度set key value [key value…]设置 key 的值是 valueO(k), k 是键个数get key获取 key 的值O(1)del key [key …]删除指定的 keyO(k), k 是键个数mset key value [key value …]批量设置指定的 key 和 valueO(k),…

开启modbus tcp模拟调试

1、新建modbus tcp服务器 ‌功能差异‌‌客户端功能‌&#xff1a; 生成并发送Modbus请求报文&#xff08;如功能码03读取寄存器&#xff09;。‌‌ 解析服务器响应数据&#xff0c;实现远程监控或控制。‌‌ ‌服务器端功能‌&#xff1a; 监听默认端口&#xff08;如502&…

昇思+香橙派 AI 开发实践:DeepSeek 全流程指南(基于 openEuler)

一、 环境准备 1. 镜像烧录 镜像烧录可以在任何操作系统内执⾏&#xff0c;这⾥以在Windows系统为例&#xff0c;使用balenaEtcher⼯具&#xff0c;快速烧录镜像到Micro SD卡中。 本章节所需的软/硬件如下&#xff1a; 软件相关&#xff1a;balenaEtcher制卡⼯具、openEul…

AI生成邮件发送脚本(带附件/HTML排版)与定时爬取网站→邮件通知(价格监控原型)

想象一下&#xff1a;每天早晨咖啡还没喝完&#xff0c;你的邮箱就自动收到了心仪商品的最新价格&#xff1b;重要报告准时带着专业排版的附件发送到客户手中——这一切不需要你手动操作。本文将用不到100行代码带你实现这两个自动化神器&#xff01; 一、为什么我们需要自动化…

【vLLM 学习】Encoder Decoder Multimodal

vLLM 是一款专为大语言模型推理加速而设计的框架&#xff0c;实现了 KV 缓存内存几乎零浪费&#xff0c;解决了内存管理瓶颈问题。 更多 vLLM 中文文档及教程可访问 →https://vllm.hyper.ai/ *在线运行 vLLM 入门教程&#xff1a;零基础分步指南 源码 examples/offline_inf…

【MySQL笔记】视图

目录一、什么是视图&#xff1f;二、使用视图的优势三、视图的创建与使用四、不能更新视图的场景五、删除视图六、总结一、什么是视图&#xff1f; 视图&#xff08;View&#xff09;是一种虚拟表&#xff0c;不存储实际数据&#xff0c;而是通过执行预定义的查询动态生成数据…

【RK3576】【Android14】分区划分

获取更多相关的【RK3576】【Android14】驱动开发&#xff0c;可收藏系列博文&#xff0c;持续更新中&#xff1a; 【RK3576】Android 14 驱动开发实战指南

Datawhale 25年7月组队学习coze-ai-assistant Task1学习笔记:动手实践第一个AI Agent—英伦生活口语陪练精灵

Chap1 了解AI工作流 1.1什么是工作流 工作流 就像是一条流水线&#xff0c;把复杂的任务拆分成多个简单的步骤&#xff0c;每一步都有明确的目标和流程。1.2智能体和工作流的区别 智能体&#xff08;AI Agent&#xff09; **是什么 &#xff1a;**智能体是一个自动化的“助手”…