Skip to content

Spring 事件机制(ApplicationEvent/Listener/Publisher)源码级

问题

Spring 的事件发布-监听机制是如何实现的?基于什么模式?

面试回答

核心答案

Spring 事件机制基于观察者模式,核心组件有三个:

  1. ApplicationEvent——事件体,继承自 EventObject,封装事件源和携带的数据。
  2. ApplicationListener——监听器接口,回调方法 onApplicationEvent(event),接收特定类型的事件。
  3. ApplicationEventPublisher——事件发布器,ApplicationContext 默认实现了该接口,任何 Spring 管理的 bean 都可以注入它来发布事件。

事件发布流程(简化版)

publishEvent(event)
  → 获取 ApplicationEventMulticaster(默认 SimpleApplicationEventMulticaster)
  → 遍历所有匹配的 ApplicationListener
  → 调用 invokeListener() 执行 onApplicationEvent 回调

Spring 支持同步异步两种模式:默认是同步的——发布线程会阻塞直到所有监听器执行完毕;通过 @Async 注解可以将监听器改为异步执行。

代码示例

java
// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Long orderId;
    private final String userId;

    public OrderCreatedEvent(Object source, Long orderId, String userId) {
        super(source);
        this.orderId = orderId;
        this.userId = userId;
    }

    public Long getOrderId() { return orderId; }
    public String getUserId() { return userId; }
}

// 2. 发布事件
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void createOrder(OrderDTO dto) {
        // 业务逻辑:创建订单
        Long orderId = saveToDb(dto);
        // 发布事件
        publisher.publishEvent(new OrderCreatedEvent(this, orderId, dto.getUserId()));
    }
}

// 3. 监听事件(同步方式)
@Component
public class OrderEventListener {

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 发送短信通知
        smsService.send(event.getUserId(), "您的订单 " + event.getOrderId() + " 已创建");
        // 记录日志
        log.info("Order created: {}", event.getOrderId());
    }
}

// 4. 监听事件(异步方式)
@Component
public class OrderAsyncEventListener {

    @Async
    @EventListener
    public void handleOrderCreatedAsync(OrderCreatedEvent event) {
        // 发送邮件通知(异步执行,不阻塞主流程)
        emailService.send(event.getUserId(), "订单创建成功", "订单号: " + event.getOrderId());
    }
}

源码分析

事件发布的入口AbstractApplicationContext.publishEvent()

java
// AbstractApplicationContext.java
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    // 包装为标准 ApplicationEvent
    ApplicationEvent applicationEvent = (event instanceof ApplicationEvent)
        ? (ApplicationEvent) event : new PayloadApplicationEvent<>(this, event);

    // 获取 ApplicationEventMulticaster
    getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}

默认的广播器SimpleApplicationEventMulticaster,它的 multicastEvent 方法会遍历所有注册的监听器,逐一调用:

java
// SimpleApplicationEventMulticaster.java
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null) ? eventType : resolveDefaultEventType(event);
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            // 异步执行
            executor.execute(() -> invokeListener(listener, event));
        } else {
            // 同步执行(默认)
            invokeListener(listener, event);
        }
    }
}

关键点:getTaskExecutor() 返回 null 时(默认情况),所有监听器在发布线程中顺序同步执行。如果设置了 Executor(比如 SimpleAsyncTaskExecutor),则异步执行。

监听器注册机制

监听器的注册发生在容器启动阶段。AbstractApplicationContext.refresh() 中的 registerListeners() 步骤:

java
protected void registerListeners() {
    // 1. 注册静态指定的监听器
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 2. 从 BeanDefinition 中注册监听器 bean
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // 3. 发布早期事件(容器启动过程中发布的事件,等待监听器注册完成后补发)
    Set<ApplicationEvent> earlyEventsToProcess = getEarlyApplicationEvents();
    // ...
}

纵深问题

1. @TransactionalEventListener 的事务阶段控制

@TransactionalEventListener@EventListener 多了阶段控制,支持 AFTER_COMMIT(默认)、AFTER_ROLLBACKAFTER_COMPLETIONBEFORE_COMMIT 四个阶段:

java
@Component
public class OrderTransactionEventListener {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleAfterCommit(OrderCreatedEvent event) {
        // 事务提交后才执行——适合发送 MQ 消息、推送通知等
        // 如果事务回滚了,这里不会执行
        mqProducer.send("order.created", event.getOrderId());
    }

    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleAfterRollback(OrderCreatedEvent event) {
        // 事务回滚后执行——适合发送告警、记录失败日志
        alertService.send("订单创建失败: " + event.getOrderId());
    }
}

实现原理TransactionSynchronizationManager 注册了一个 TransactionSynchronization,在事务提交/回滚完成后回调,触发监听器执行。核心在 TransactionalEventListenerFactoryApplicationListenerMethodTransactionalAdapter

典型场景:业务方法执行成功后发送 MQ 消息。如果不用事务事件,方法执行完直接发 MQ,但事务随后回滚了,MQ 消息就变成了"脏消息"。用 @TransactionalEventListener(phase = AFTER_COMMIT) 可以保证事务提交后消息才发出。

2. 同步事件发布的异常风险

问题:如果监听器抛出异常,发布者会怎样?

默认同步模式下,监听器抛出的异常会直接穿透到 publishEvent() 方法,导致发布者后续逻辑无法执行。比如:

java
@Service
public class OrderService {
    public void createOrder(OrderDTO dto) {
        Long orderId = saveToDb(dto);
        publisher.publishEvent(new OrderCreatedEvent(this, orderId, dto.getUserId()));
        // 如果监听器抛异常,这行不会执行
        log.info("Order created successfully");
    }
}

解决方案

  • 方案 A:监听器内部 try-catch,不要抛异常到外部
  • 方案 B:设置 SimpleApplicationEventMulticasterErrorHandler,这样可以捕获异常但不中断执行
  • 方案 C:使用 @Async 异步化监听器,异常不会影响发布线程

3. 事件发布顺序控制

多个监听器监听同一事件时,执行顺序由 @Order 注解控制:

java
@Component
public class PriorityListener {

    @EventListener
    @Order(1)  // 数字越小越优先
    public void highPriority(OrderCreatedEvent event) {
        // 先执行
    }

    @EventListener
    @Order(10)
    public void lowPriority(OrderCreatedEvent event) {
        // 后执行
    }
}

同一个监听器类中多个事件处理方法,执行顺序由 Java 反射返回的方法数组顺序决定,不可靠。所以不要把多个 @EventListener 放在同一个类中,如果需要顺序控制,拆成多个监听器类。

4. 事件类型与泛型事件

Spring 4.2+ 支持泛型事件,通过 PayloadApplicationEvent 包装任意对象:

java
// 发布泛型事件
publisher.publishEvent(new PayloadApplicationEvent<>(this, "任意类型"));

// 监听——直接监听类型,不需要继承 ApplicationEvent
@EventListener
public void handleStringEvent(String data) {
    System.out.println("收到字符串事件: " + data);
}

这种方式更轻量,适合不需要继承体系的场景。

总结

Spring 事件机制的核心是观察者模式,通过 ApplicationEvent + ApplicationListener + ApplicationEventPublisher 三个角色实现发布-订阅解耦。关键设计点:

  • 同步 vs 异步:默认同步,通过 @Async 或自定义 TaskExecutor 改为异步
  • @TransactionalEventListener:事务边界控制,避免事务回滚后执行副作用操作
  • 异常处理:同步模式下监听器异常会穿透到发布者,需要做好防御
  • 性能:事件机制适合非关键路径的解耦,不适合高频调用(如每次请求都发布事件),高频场景建议用消息队列替代

参考资料:Spring 源码 — AbstractApplicationContext.publishEvent()、SimpleApplicationEventMulticaster;《Spring 源码深度解析》

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。