)
第81篇Spring事件机制2026版系列导航《Java 100 天进阶之路》完整目录 |⬅️ 上一篇第80篇Spring循环依赖解决 |➡️ 下一篇第82篇Spring面试压轴题️ 本文阅读地图3 分钟速览第80篇搞定了循环依赖本篇深入Spring事件机制。你写的代码里用户注册后要发邮件、更新积分、记录日志——这些“副作用”如果都写在注册方法里代码会越来越臃肿。Spring事件机制就是解决这个问题的优雅方案。搞懂Spring事件机制就是搞懂了如何用“发布-订阅”模式写出松耦合、易扩展的代码模块核心问题一句话回答事件机制是什么怎么让组件之间松耦合通信基于观察者模式的发布-订阅模型——发布者只管发布监听者按需接收三大核心组件事件机制由哪三部分组成事件Event、发布者Publisher、监听器Listener怎么用代码怎么写定义事件类 →EventListener监听 →publishEvent()发布同步 vs 异步事件默认同步还是异步默认同步——发布者会阻塞等待所有监听器执行完毕事务事件事务没提交就发事件会怎样监听器查DB读不到最新数据或事务回滚后事件无法撤回和MQ的区别能代替消息队列吗不能。本地事件只在单JVM内生效跨服务还得用MQ面试最爱问高频考点有哪些见文末 面试 小节一、核心知识点1. 什么是Spring事件机制Spring事件机制是Spring框架中用以支持应用内组件间解耦的发布-订阅模型允许对象间进行松耦合通信。它基于经典的观察者模式实现让组件之间“不直接打招呼”而是通过“广播”和“收听”的方式协作。核心思想发布者只管发布监听者按需接收彼此互不知晓。就像广播电台和收音机——电台只管播内容不关心谁在听收音机只管收信号不关心谁在播。2. 什么时候用事件机制场景传统做法的问题事件驱动的优势用户注册后发邮件、更新积分、记日志注册方法里写一大堆调用越改越臃肿注册方法只发布事件各监听器独立处理订单支付后扣库存、生成运单、发券多个服务顺序调用耦合度高各监听器独立演进互不影响审计日志记录每个Service方法里都要写日志统一监听业务事件一处定义处处生效3. 事件机制 vs 消息队列MQ对比维度Spring事件机制消息队列Kafka/RabbitMQ作用范围单JVM内同一个应用跨JVM、跨服务延迟零延迟无序列化开销有网络延迟和序列化开销持久化❌ 不持久重启即失✅ 消息持久化可靠性无重试机制有重试、死信等机制适用场景应用内轻量级解耦跨服务通信、削峰填谷一句话总结Spring事件机制不是MQ的替代品而是互补品。本地事件保证事务一致性分布式事件实现跨服务通知。4. Spring Boot 3.x中的变化Spring Boot 3.x在事件机制方面保持了对Spring Framework 6.x的兼容主要变化包括从Spring 4.2开始事件类不再强制继承ApplicationEvent任意POJO均可作为事件EventListener注解支持泛型事件和条件过滤TransactionalEventListener支持响应式事务管理二、通俗讲解1分钟开心学把Spring事件机制想象成“微信朋友圈”你发了一条朋友圈发布事件publishEvent你的朋友看到了监听器收到事件有人点赞、有人评论、有人转发不同的监听器做不同的事关键点你发朋友圈时不用挨个每个人说“来看我朋友圈”——发布者只负责发布。朋友想点赞就点赞不想看就划走——监听器按需响应互不影响。新加了一个朋友不用通知所有人——新增监听器无需修改发布者代码。把核心组件对应起来朋友圈场景Spring事件机制朋友圈内容事件Event发朋友圈的人事件发布者ApplicationEventPublisher刷朋友圈的人事件监听器ApplicationListener微信服务器推送事件多播器ApplicationEventMulticaster三、核心组件详解Spring事件机制基于观察者模式由三大核心组件构成3.1 事件Event—— 消息载体事件是承载业务数据的对象。在Spring 4.2之前所有自定义事件必须继承ApplicationEvent类。从Spring 4.2开始任意POJO均可作为事件不再强制继承。// 方式一继承ApplicationEvent传统方式publicclassUserRegisteredEventextendsApplicationEvent{privatefinalStringuserId;privatefinalStringemail;publicUserRegisteredEvent(Objectsource,StringuserId,Stringemail){super(source);this.userIduserId;this.emailemail;}// getters...}// 方式二POJO即可Spring 4.2推荐publicclassUserRegisteredEvent{privatefinalStringuserId;privatefinalStringemail;publicUserRegisteredEvent(StringuserId,Stringemail){this.userIduserId;this.emailemail;}// getters...}命名建议事件命名应使用过去时态表示“已经发生的事情”——如OrderPaidEvent、UserRegisteredEvent而不是SendEmailEvent命令式。3.2 事件发布者Publisher—— 触发事件ApplicationEventPublisher是事件发布的入口接口publicinterfaceApplicationEventPublisher{defaultvoidpublishEvent(ApplicationEventevent){publishEvent((Object)event);}voidpublishEvent(Objectevent);}ApplicationContext接口继承了ApplicationEventPublisher所以任何Bean都可以通过注入ApplicationEventPublisher来发布事件。ServicepublicclassUserService{AutowiredprivateApplicationEventPublisherpublisher;publicvoidregister(StringuserId,Stringemail){// 核心业务逻辑userDao.save(newUser(userId,email));// 发布事件——不关心谁处理publisher.publishEvent(newUserRegisteredEvent(userId,email));}}3.3 事件监听器Listener—— 处理事件Spring提供了两种定义监听器的方式方式一实现ApplicationListener接口传统ComponentpublicclassEmailListenerimplementsApplicationListenerUserRegisteredEvent{OverridepublicvoidonApplicationEvent(UserRegisteredEventevent){// 发送欢迎邮件sendEmail(event.getEmail());}}方式二EventListener注解推荐Spring 4.2ComponentpublicclassUserEventListener{EventListenerpublicvoidsendWelcomeEmail(UserRegisteredEventevent){// 发送欢迎邮件}EventListenerpublicvoidaddBonusPoints(UserRegisteredEventevent){// 增加积分}}EventListener的优势方法级监听一个类可以监听多个事件代码更简洁支持条件过滤和异步。四、事件多播器ApplicationEventMulticaster4.1 多播器的作用ApplicationEventMulticaster是Spring事件机制的“广播器”——负责管理所有监听器列表并在事件发布时将事件分发给所有匹配的监听器。发布者 → publishEvent() → 多播器 → 遍历监听器 → 匹配类型 → 触发监听器Spring的默认实现是SimpleApplicationEventMulticaster它默认在调用线程中同步执行所有监听器。4.2 多播器的初始化AbstractApplicationContext.initApplicationEventMulticaster()负责初始化多播器如果容器中有名为applicationEventMulticaster的Bean使用自定义的多播器否则创建默认的SimpleApplicationEventMulticaster实例4.3 监听器的注册AbstractApplicationContext.registerListeners()负责注册监听器直接注册applicationListeners属性中保存的监听器注册applicationListenerBeans属性中保存的Bean名称支持懒加载EventListener的解析EventListenerMethodProcessor在容器启动时扫描所有Bean查找带有EventListener注解的方法为每个方法创建一个ApplicationListener代理对象并注册到多播器。五、事件发布与监听完整流程5步源码入口AbstractApplicationContext.publishEvent()→SimpleApplicationEventMulticaster.multicastEvent()六、高级特性6.1 异步事件默认情况下Spring事件是同步的——发布者线程会阻塞直到所有监听器处理完毕。开启异步的步骤// 1. 在配置类或启动类上添加 EnableAsyncSpringBootApplicationEnableAsyncpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}// 2. 在监听器方法上添加 AsyncComponentpublicclassUserEventListener{AsyncEventListenerpublicvoidsendWelcomeEmail(UserRegisteredEventevent){// 这个方法会在独立线程中执行不阻塞主流程}}注意事项异步监听器抛出的异常不会传播回发布者需要自行处理日志和告警。6.2 条件监听Conditional Event Listening通过EventListener的condition属性可以用SpEL表达式控制监听器是否执行ComponentpublicclassUserEventListener{// 只处理VIP用户的注册事件EventListener(condition#event.vip true)publicvoidhandleVipUser(UserRegisteredEventevent){// VIP用户专属处理}}6.3 事务绑定事件TransactionalEventListener这是面试高频考点。经典问题是你在事务里发布了事件监听器立刻执行但事务还没提交监听器去查数据库读不到最新数据或者事务回滚了但事件已经发出去了无法撤回。解决方案使用TransactionalEventListener让事件在指定的事务阶段触发。ComponentpublicclassOrderEventListener{// 只在事务成功提交后执行最常用TransactionalEventListener(phaseTransactionPhase.AFTER_COMMIT)publicvoidhandleOrderPaid(OrderPaidEventevent){// 此时事务已提交可以安全地查询最新数据// 发消息、扣库存等操作}// 事务回滚后执行清理操作TransactionalEventListener(phaseTransactionPhase.AFTER_ROLLBACK)publicvoidhandleRollback(OrderPaidEventevent){// 事务回滚后的补偿逻辑}}事务阶段枚举阶段说明使用场景BEFORE_COMMIT事务提交前需要在提交前执行的逻辑AFTER_COMMIT事务提交后推荐确保数据已持久化后再处理AFTER_ROLLBACK事务回滚后回滚后的补偿逻辑AFTER_COMPLETION事务完成后提交或回滚无论成功失败都要执行重要提示如果事件不是在活跃事务中发布的TransactionalEventListener默认不会执行。可以通过设置fallbackExecution true让它在无事务时也执行。七、最佳实践实践建议说明来源事件命名用过去时态OrderShippedEvent、PaymentFailedEvent反映“已发生的事实”事件类设计为不可变使用final字段 构造器初始化事件数据最小化只包含必要字段避免传递大对象高频事件用异步批量避免频繁触发影响性能异步监听器自己处理异常异步异常不会传播给发布者避免在事件中做太重操作事件适合轻量级副作用重操作应走MQ事务事件用AFTER_COMMIT确保数据一致性八、避坑要点错误/误区后果正确做法在事务中发布事件但监听器立即处理查不到最新数据 / 事务回滚后事件无法撤回用TransactionalEventListener绑定事务阶段在监听器中做耗时操作阻塞主流程响应变慢用Async异步处理或走MQ异步监听器不处理异常异常被吞掉问题难以排查在异步方法中自行捕获并记录日志在事件中传递大对象或敏感信息内存占用大有安全风险只传递必要字段如ID监听器自行查询把事件机制当MQ用事件丢失、无法跨服务跨服务用MQ本地解耦用事件忘记在监听器类上加Component监听器不会被Spring管理事件不触发确保监听器类是Spring Bean事务事件在无事务时发布监听器不执行默认行为确认发布者在事务中或设置fallbackExecution true九、面试高频考点Q1Spring事件机制的底层原理是什么Spring事件机制基于观察者模式实现。核心流程是业务组件通过ApplicationEventPublisher.publishEvent()发布事件事件被交给ApplicationEventMulticaster事件多播器多播器遍历所有已注册的监听器根据事件类型筛选匹配的监听器然后依次触发其处理逻辑。ApplicationContext本身实现了ApplicationEventPublisher接口所以可以直接发布事件。Q2Spring事件默认是同步还是异步怎么改成异步默认是同步的——发布者线程会阻塞直到所有监听器处理完毕。改成异步需要两步①在配置类或启动类上添加EnableAsync注解②在监听器方法上添加Async注解。异步监听器的异常不会传播回发布者需要自行处理。Q3EventListener和ApplicationListener有什么区别ApplicationListener是接口方式需要实现onApplicationEvent方法EventListener是注解方式Spring 4.2直接标注在方法上即可。EventListener更简洁——一个类可以监听多个事件支持SpEL条件过滤是推荐方式。Q4TransactionalEventListener的作用是什么将事件监听器绑定到事务的特定阶段。最常用的是AFTER_COMMIT——只在事务成功提交后才触发监听器。这样可以避免“事务未提交时监听器查不到最新数据”或“事务回滚后事件无法撤回”的问题。默认只在活跃事务中生效可通过fallbackExecution true在无事务时也执行。Q5Spring事件机制和消息队列MQ有什么区别Spring事件机制是应用内的轻量级发布-订阅只在单JVM内生效无持久化、无重试。MQ是跨服务的分布式消息系统支持持久化、重试、死信等机制。两者不是替代关系而是互补——本地事件保证事务一致性分布式事件实现跨服务通知。 面试官追问陷阱加分题追问1“EventListener的condition属性底层是怎么实现的”condition属性支持SpEL表达式Spring Expression Language。Spring在运行时解析表达式根据#event.xxx等变量动态计算布尔值决定是否执行该监听器。表达式中可以引用事件对象的字段、Spring Bean等。追问2“TransactionalEventListener的AFTER_COMMIT和直接Async有什么区别” 两者解决的问题不同。Async解决的是同步阻塞问题——让监听器在独立线程中执行不阻塞主流程。TransactionalEventListener解决的是事务一致性问题——确保事件在事务提交后才处理避免读到未提交的数据或事务回滚后事件无法撤回。两者可以组合使用——AsyncTransactionalEventListener(phase AFTER_COMMIT)既保证事务一致性又实现异步处理。追问3“如果同一个事件有多个监听器执行顺序怎么控制” 可以使用Order注解控制执行顺序。数值越小优先级越高。不过需要注意的是如果监听器之间没有顺序依赖尽量不要依赖顺序保持监听器之间的独立性。十、练习题简答题Spring事件机制由哪三大核心组件组成各自的作用是什么代码题实现一个“订单支付成功”的事件驱动流程——定义OrderPaidEvent在OrderService中发布事件分别实现“扣减库存”和“发送通知”两个监听器一个用ApplicationListener接口一个用EventListener注解。分析题某项目中用户在注册后立即查询积分但发现积分没有增加。代码中注册方法发布了UserRegisteredEvent监听器在收到事件后增加了积分。请分析可能的原因并给出解决方案。 你的学习进度当前第81篇 / 共108篇 ·进阶篇Spring全家桶第73~82篇✅ 已完成基础篇44篇 第45~81篇 正在学第81篇⏳ 待学习第82~108篇 完整目录 学习指南 | 订阅本专栏不错过每一篇 下一篇文章预告下一篇《第82篇Spring面试压轴题》内容简介Spring全家桶专题收官之作——IoC、AOP、事务、MVC、循环依赖、事件机制等核心考点20问速查大厂面试高频压轴题精讲。Spring全家桶专题收官拿下所有面试考点《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇建议收藏 关注一起100天拿offer 点击关注我更新后第一时间收到推送