事件机制:@EventListener 与事务事件
结论先行:Spring 事件是容器内的观察者模式实现:发布方只调 ApplicationEventPublisher.publishEvent,监听方用 @EventListener 解耦响应。默认监听器是同步执行的(同线程、异常会向发布方传播,影响主流程),需要异步时配合 @Async 与独立线程池。@TransactionalEventListener 让监听动作延迟到事务提交/回滚之后再执行,解决"事务还没提交就发事件、监听方读不到数据"的经典问题。
一、三个角色
- 事件对象:可继承 ApplicationEvent,也可用任意 POJO(Spring 4.2+ 按泛型自动推断),推荐 record/普通类;
- 发布者:注入 ApplicationEventPublisher,调 publishEvent(event);
- 监听者:@EventListener 标注方法,方法参数即事件类型,支持 SpEL condition 过滤。
public record OrderCreated(Long orderId, Long userId) { } // 事件对象
@Component
public class OrderPublisher {
private final ApplicationEventPublisher publisher;
public void create(Order o) {
// 业务落库(事务内)
publisher.publishEvent(new OrderCreated(o.getId(), o.getUserId())); // 发布
}
}
@Component
public class OrderListener {
@EventListener // 默认同步执行
public void sendNotice(OrderCreated e) { /* 发短信/站内信 */ }
@TransactionalEventListener(
phase = TransactionPhase.AFTER_COMMIT, // 事务提交成功后才执行
fallbackExecution = true) // 无事务时也照常执行
public void afterCommit(OrderCreated e) { /* 发 MQ:保证事务已提交 */ }
}
二、要点清单
- 同步监听抛异常默认中断主流程 → 关键监听要自行 try-catch,或用 @Async(配 AsyncUncaughtExceptionHandler 收异常);
- 多个监听器执行顺序:@Order 控制;
- @TransactionalEventListener 的 phase:BEFORE_COMMIT / AFTER_COMMIT(默认)/ AFTER_ROLLBACK / AFTER_COMPLETION;
- 与 MQ 的区别:Spring 事件默认是 JVM 内同步解耦;MQ 是跨进程异步削峰。经典组合"事务提交成功后发 MQ"就靠 AFTER_COMMIT 事件触发。
常见追问 / 记忆点
- 追问 1:为什么要在事务提交后发事件/消息?答:若在事务内直接发 MQ,消息到达消费者时事务可能还没提交,消费者查库查不到数据(或事务最终回滚导致消息与数据不一致)。
- 追问 2:@TransactionalEventListener 一定可靠吗?答:AFTER_COMMIT 若回调本身失败仍可能丢动作,关键场景要配重试/本地消息表。
- 记忆点:"发布只管发、监听各自干;默认同步会传染异常;要等提交完再动作就用事务事件"。