Spring 事件监听机制

候选人小徐在面试美团 P6 时,面试官问道:

"Spring 的事件监听机制用过吗?"

小徐说:"用过,用过 @EventListener..."

面试官追问:"@EventListener 和 ApplicationListener 有什么区别?"

小徐说:"呃...好像差不多..."

面试官继续追问:"事件监听在 Spring Boot 中有什么应用?"

小徐答不上来了。

【面试官心理】 这道题我用来测试候选人对 Spring 事件驱动模型的理解。Spring Boot 的很多功能(@RefreshScope、@Async 配置、Spring Cloud 的配置刷新)都基于事件机制。知道怎么用不重要,知道为什么用才是关键。


一、核心问题 🔴

1.1 问题拆解

第一层:基础

  • "Spring 事件机制有哪些核心组件?"
  • "ApplicationEvent 和 ApplicationListener 是什么关系?"

第二层:使用

  • "@EventListener 和 ApplicationListener 有什么区别?"
  • "SmartApplicationListener 是什么?"
  • "@TransactionalEventListener 有什么用?"

第三层:原理

  • "Spring 事件是怎么发布的?"
  • "ApplicationEventMulticaster 是什么?"

第四层:应用

  • "Spring Boot 中事件机制有哪些应用?"
  • "如何在事件监听中安全地访问数据库?"

1.2 ❌ 错误示范

候选人原话 A:"Spring 事件就是发布-订阅模式,没什么的。"

问题诊断

  • 知道大概模式,但不了解具体组件
  • 说不清 ApplicationEventMulticaster 的作用

候选人原话 B:"@EventListener 是新写法,ApplicationListener 是老写法,功能一样。"

问题诊断

  • 知道注解方式更新,但不理解区别
  • 不知道 @EventListener 支持条件过滤

候选人原话 C:"事件监听可以用来解耦,没什么特别的。"

问题诊断

  • 知道用途,但说不清和直接调用的区别
  • 不理解事务和事件的关系

1.3 标准回答

P5 回答:核心组件

Spring 事件机制包含三个核心组件:

组件作用说明
ApplicationEvent事件对象继承它定义自己的事件
ApplicationListener监听器接口监听特定事件
ApplicationEventPublisher事件发布器发布事件
// 1. 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order;
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    public Order getOrder() { return order; }
}

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

    public void createOrder(Order order) {
        orderRepository.save(order);
        // 发布事件
        publisher.publishEvent(new OrderCreatedEvent(this, order));
    }
}

// 3. 监听事件
@Service
public class OrderEventListener {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        Order order = event.getOrder();
        // 发送通知、发送消息等
        emailService.sendOrderConfirmation(order);
    }
}

1.4 追问升级

追问 1:@EventListener vs ApplicationListener

// ApplicationListener 方式(Spring 早期方式)
@Service
public class MyListener implements ApplicationListener<OrderCreatedEvent> {
    @Override
    public void onApplicationEvent(OrderCreatedEvent event) {
        // 处理事件
    }
}

// @EventListener 方式(推荐方式)
@Service
public class MyListener {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 处理事件
    }
}
对比@EventListenerApplicationListener
代码量少(只需注解)多(需要实现接口)
条件过滤支持 condition 属性不支持
事务绑定支持 @TransactionalEventListener不支持
异步执行支持 @Async需要手动配置
执行顺序支持 @Order支持 Ordered

@EventListener 的高级特性:

@Service
public class AdvancedListener {
    // 条件过滤:只有 order 金额大于 1000 才处理
    @EventListener(condition = "#event.order.amount > 1000")
    public void handleHighValueOrder(OrderCreatedEvent event) {
        // 只有高价值订单才处理
    }

    // 事务绑定:事务提交后才执行
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleAfterCommit(OrderCreatedEvent event) {
        // 只有事务提交后才执行
        // 适合发送通知、消息等
    }

    // 异步执行
    @Async
    @EventListener
    public void handleAsync(OrderCreatedEvent event) {
        // 异步处理,不阻塞主线程
    }

    // 指定顺序
    @Order(1)
    @EventListener
    public void handleFirst(OrderCreatedEvent event) {
        // Order 小的先执行
    }
}

追问 2:事件发布原理

// ApplicationEventPublisher 接口
public interface ApplicationEventPublisher {
    void publishEvent(ApplicationEvent event);
    void publishEvent(Object event); // Spring 4.2+ 支持任意对象
}

// 源码实现
// AbstractApplicationContext.publishEvent()
protected void publishEvent(Object event, ResolvableType eventType) {
    // 1. 将事件包装成 ApplicationEvent
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    } else {
        applicationEvent = new PayloadApplicationEvent<>(this, event);
    }

    // 2. 交给 ApplicationEventMulticaster 处理
    getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}

// ApplicationEventMulticaster 多播
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
    // 1. 获取匹配的监听器
    ApplicationListener<?>[] listeners = getApplicationListeners(event, eventType);

    // 2. 依次调用每个监听器
    for (ApplicationListener<?> listener : listeners) {
        invokeListener(listener, event);
    }
}
事件发布流程:

publisher.publishEvent(event)
  └─ AbstractApplicationContext.publishEvent()
      └─ ApplicationEventMulticaster.multicastEvent()
          └─ 遍历匹配的 ApplicationListener
              └─ invokeListener()
                  └─ listener.onApplicationEvent(event)

追问 3:SmartApplicationListener 和执行顺序

// SmartApplicationListener 可以指定监听顺序和事件类型
@Component
public class OrderedListener implements SmartApplicationListener {
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        // 指定监听的事件类型
        return OrderCreatedEvent.class.isAssignableFrom(eventType);
    }

    @Override
    public int getOrder() {
        // 返回数字越小,优先级越高
        return 1;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        // 处理事件
    }
}
💡

SmartApplicationListenerApplicationListener 更精确:

  • ApplicationListener 通过 onApplicationEvent() 接收所有事件,需要自己判断
  • SmartApplicationListener 通过 supportsEventType() 精确指定,只接收感兴趣的事件 :::

追问 4:@TransactionalEventListener 的事务绑定

这是理解 Spring 事件机制的关键——事件监听默认和发布者在同一个事务中:

@Service
public class OrderService {
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        publisher.publishEvent(new OrderCreatedEvent(order));
        // 此时事件监听器和这个方法在同一个事务中
        // 如果后续代码抛异常,整个事务回滚
        // 包括事件监听器已经执行的操作
    }
}

@Service
public class NotificationService {
    @EventListener  // ❌ 问题
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 如果 OrderService 后续代码抛异常
        // 这里已经执行的逻辑也会回滚
        // 比如:发送短信成功了,但订单因为后续逻辑失败而回滚了
        // 短信就白发了
    }
}

// ✅ 正确做法:使用 @TransactionalEventListener
@Service
public class NotificationService {
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 只有事务提交后才执行
        // 避免了事件发出但事务回滚的问题
    }
}

@TransactionalEventListener 的四个 phase:

public enum TransactionPhase {
    BEFORE_COMMIT,   // 事务提交前
    AFTER_COMMIT,    // 事务提交后(最常用)
    AFTER_ROLLBACK,  // 事务回滚后
    AFTER_COMPLETION // 事务完成后(提交或回滚)
}

二、延伸问题 🟡

2.1 Spring Boot 中的事件应用

Spring Boot 大量使用事件机制:

// SpringApplicationRunListener 监听应用启动事件
public class MyListener implements SpringApplicationRunListener {
    @Override
    public void started(ConfigurableApplicationContext context) {
        // 应用启动完成后执行
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        // 应用正在运行
    }
}

// ContextRefreshedEvent:容器刷新完成
@EventListener
public void handleContextRefreshed(ContextRefreshedEvent event) {
    // 容器初始化完成后执行
    // 常用于初始化某些 Bean
}

// ApplicationReadyEvent:应用准备就绪
@EventListener
public void handleApplicationReady(ApplicationReadyEvent event) {
    // 应用已经准备好接收请求
}

// Spring Cloud Config: RefreshEvent
@EventListener
public void handleRefresh(RefreshEvent event) {
    // 配置刷新时触发
    // @RefreshScope 的 Bean 会重新创建
}

2.2 异步事件处理

// 配置异步事件
@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// 使用 @Async
@Service
public class NotificationService {
    @Async
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 异步处理,不阻塞主线程
        sendEmail(event.getOrder());
    }
}

:::warning ⚠️ 异步事件监听有一个问题:如果主事务回滚,异步监听器可能已经开始执行了(取决于线程调度)。如果监听器依赖主事务的结果,可能会有问题。解决方法是结合 @TransactionalEventListener(phase = AFTER_COMMIT)

2.3 泛型事件

// Spring 4.2+ 支持泛型事件
@Service
public class GenericEventPublisher {
    @Autowired
    private ApplicationEventPublisher publisher;

    public void publishResult(String taskId, Result result) {
        // 直接发布泛型对象,不需要包装
        publisher.publishEvent(new ResultEvent<>(this, taskId, result));
    }
}

// 定义泛型事件
public class ResultEvent<T> extends ApplicationEvent {
    private final String taskId;
    private final T result;

    public ResultEvent(Object source, String taskId, T result) {
        super(source);
        this.taskId = taskId;
        this.result = result;
    }
}

// 监听泛型事件
@Service
public class ResultHandler {
    @EventListener
    public void handleResult(ResultEvent<User> event) {
        // 只处理 Result 是 User 类型的事件
        User result = event.getResult();
    }

    @EventListener
    public void handleUserResult(ResultEvent<?> event) {
        // 处理所有类型的 ResultEvent
    }
}

三、生产避坑

3.1 事件循环监听

@Service
public class ServiceA {
    @Autowired
    private ApplicationEventPublisher publisher;

    @EventListener
    public void handleEvent(Event1 e) {
        publisher.publishEvent(new Event2());
    }
}

@Service
public class ServiceB {
    @EventListener
    public void handleEvent(Event2 e) {
        publisher.publishEvent(new Event1());
    }
}

// ❌ 可能导致事件循环:Event1 → Event2 → Event1 → ...

解决:使用 @ApplicationListener 的条件判断或 applicationContext.containsBean() 检查。

3.2 事件顺序问题

// 如果多个监听器依赖执行顺序
@Service
public class ListenerA {
    @Order(1)
    @EventListener
    public void handleFirst(OrderCreatedEvent event) {
        // 先执行
    }
}

@Service
public class ListenerB {
    @Order(2)
    @EventListener
    public void handleSecond(OrderCreatedEvent event) {
        // 后执行
        // 但如果 ListenerA 抛异常,ListenerB 可能不会执行
        // 因为默认是同步执行
    }
}

3.3 事件与异常处理

// 如果监听器抛异常,默认会阻止后续监听器执行
@Service
public class ListenerA {
    @EventListener
    public void handle(OrderCreatedEvent event) {
        throw new RuntimeException("失败");
    }
}

@Service
public class ListenerB {
    @EventListener
    public void handle(OrderCreatedEvent event) {
        // 不会执行,因为 A 抛异常了
    }
}

// 使用 ErrorHandler 处理异常
@Component
public class MyErrorHandler implements ErrorHandler {
    @Override
    public void handleError(Throwable t) {
        log.error("事件处理异常", t);
    }
}

@Service
public class MyListener {
    @Autowired
    private ApplicationEventPublisher publisher;

    @EventListener
    public void handle(OrderCreatedEvent event) {
        // 业务逻辑
    }
}

// 或者使用 @Async + 自定义线程池
@Configuration
public class AsyncConfig {
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster multicaster =
            new SimpleApplicationEventMulticaster();
        multicaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return multicaster;
    }
}

四、工程选型

4.1 事件 vs 直接调用

对比事件机制直接调用
耦合度低(发布者和监听器解耦)高(直接依赖)
可扩展性高(可以添加多个监听器)低(需要修改调用方)
性能有开销(需要广播)无额外开销
事务边界需要特殊处理天然在同一事务
适用场景解耦、异步处理、多方响应简单调用、无需解耦

4.2 事件机制的最佳实践

// 1. 事件使用不可变对象
public class OrderCreatedEvent extends ApplicationEvent {
    private final Order order; // final,不可变

    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
}

// 2. 监听器要快速执行,避免耗时操作
@Async
@EventListener
public void handle(OrderCreatedEvent event) {
    // 异步处理耗时操作
}

// 3. 使用事务事件监听确保一致性
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void afterCommit(OrderCreatedEvent event) {
    // 只有事务提交后才发送通知/消息
}

// 4. 给事件命名清晰的前缀
// ❌ BadEvent
// ✅ OrderCreatedEvent
// ✅ PaymentProcessedEvent

五、面试总结

Spring 事件机制是 Spring 框架中相对"高级"的功能。

P5 候选人能说出 ApplicationEvent、ApplicationListener 的基本用法。 P6 候选人能说出 @EventListener 的高级特性(条件、事务绑定、异步),能说清 @TransactionalEventListener 的价值。 P7 候选人能说出事件机制的原理(ApplicationEventMulticaster)、能分析事件和事务的关系、能说出事件循环和异常处理的问题。

记住,事件机制的核心价值是"解耦",但解耦也带来了复杂性和潜在问题。