事件机制:@EventListener 与异步事件

引言

业务里“下单成功要发短信、记日志、送积分”——若全部写在下单方法里,每次加新动作都要改主流程。事件机制把“发生什么”(事件)与“怎么响应”(监听器)解耦:主流程只 publishEvent,关心的人各自监听。本章掌握发布/监听/异步三件事。

核心概念

  • 发布端:注入 ApplicationEventPublisher,调 publishEvent(Object)——Spring 4.2 起任意 POJO 都可作事件(不是 ApplicationEvent 的会被包装成 PayloadApplicationEvent,以官方文档为准)。
  • 监听端@EventListener(org.springframework.context.event)标在 Bean 的 public 方法上,方法参数即事件类型;容器据此匹配事件。
  • 条件监听:@EventListener(condition = "#ev.success"),SpEL 里方法参数名可直接引用(详见第 18 章)。
  • 返回值进阶:监听方法有返回值时,返回值会作为新事件再次发布(可做事件链)。
  • 事务事件:@TransactionalEventListener 默认在事务 AFTER_COMMIT 后触发,避免“事务没提交就发事件”;无事务且未开 fallbackExecution 时不触发(以官方文档为准)。
  • 默认同步:发布线程逐个调用监听器,监听器异常会传回发布方。
  • 异步两种姿势:①监听方法上加 @Async + 开启 @EnableAsync;②自定义 applicationEventMulticaster Bean(SimpleApplicationEventMulticaster.setTaskExecutor(executor))统一异步。多监听器顺序用 @Order

代码示例

// 1) 事件:普通 POJO 即可
public record OrderCreated(Long orderId, boolean success) {}

// 2) 发布端
@Service
public class OrderService {
    private final ApplicationEventPublisher publisher;
    public OrderService(ApplicationEventPublisher publisher) { this.publisher = publisher; }

    public void create() {
        // ...下单逻辑...
        publisher.publishEvent(new OrderCreated(1001L, true)); // 发完即走,不关心谁听
    }
}

// 3) 监听端:一个类可挂多个监听方法
@Component
public class OrderListeners {
    @EventListener
    public void onCreated(OrderCreated ev) {
        System.out.println("记录日志 orderId=" + ev.orderId());
    }

    @EventListener(condition = "#ev.success")
    @Async // 异步执行:需 @EnableAsync
    public void sendSms(OrderCreated ev) {
        System.out.println("异步发短信 orderId=" + ev.orderId());
    }
}

// 4) 事务提交后再发通知
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,
        fallbackExecution = true)
public void onCommitted(OrderCreated ev) { /* 事务已提交才执行 */ }

注意点

  • 默认同步意味着监听器里别做慢操作,否则拖慢发布方;要快就异步化。
  • 异步监听器(@Async 或异步 multicaster)的异常不会自动传回发布方,需在监听器内部自行捕获记录(以官方文档为准)。
  • @Async 走代理,同类自调用同样失效(回顾第 13 章);同时记得配线程池执行器,别依赖框架默认的简单执行器。
  • 事务事件必须等事务真的提交才触发;误以为“方法一执行就发事件”会读到未提交数据。
  • 事件只解耦“通知”,别在监听器里反写主流程数据造成隐式依赖环。

小结

事件机制 = 发布 POJO 事件 + @EventListener 匹配订阅 + 可选条件/事务相位/异步化。默认同步、可异步、可延迟到事务提交后,是 Spring 应用内模块解耦(含跨 Bean 解环)的利器。

笔记加载中…