事件机制:@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 若回调本身失败仍可能丢动作,关键场景要配重试/本地消息表。
  • 记忆点:"发布只管发、监听各自干;默认同步会传染异常;要等提交完再动作就用事务事件"。
笔记加载中…