Skip to content

事件机制

  Spring 的事件机制是基于观察者模式实现的,用于在 Bean 之间进行解耦通信。一个 Bean 发布事件,其他 Bean 监听并处理,发布者和监听者之间不直接依赖。

一、事件机制三要素

① 事件(ApplicationEvent):
   继承 ApplicationEvent 的消息体

② 发布者(ApplicationEventPublisher):
   调用 publishEvent() 发布事件

③ 监听者(ApplicationListener / @EventListener):
   接收并处理事件

二、基本使用

java
// ① 定义事件
@Data
public class OrderCreatedEvent extends ApplicationEvent {
    private Long orderId;
    private Long userId;

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

// ② 发布事件
@Service
public class OrderService {

    @Autowired
    private ApplicationEventPublisher publisher;

    @Transactional
    public void createOrder(Order order) {
        // 下单逻辑
        orderMapper.insert(order);

        // 发布事件
        publisher.publishEvent(new OrderCreatedEvent(this, order.getId(), order.getUserId()));
    }
}

// ③ 监听事件
@Component
public class OrderEventListener {

    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("订单创建成功:" + event.getOrderId());
        // 发送短信通知
        // 增加积分
        // 记录日志
    }
}

三、@EventListener 详解

java
@Component
public class OrderEventListener {

    // ① 基本监听
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 处理逻辑
    }

    // ② 条件监听(SpEL 表达式)
    @EventListener(condition = "#event.orderId > 1000")
    public void handleBigOrder(OrderCreatedEvent event) {
        // 只处理大订单
    }

    // ③ 异步监听(需要 @EnableAsync 开启)
    @EventListener
    @Async
    public void handleAsync(OrderCreatedEvent event) {
        // 异步处理,不阻塞主线程
    }

    // ④ 监听多个事件类型
    @EventListener({OrderCreatedEvent.class, OrderCancelledEvent.class})
    public void handleOrderEvents(Object event) {
        // 处理多种事件
    }

    // ⑤ 监听 + 事务阶段(在事务提交后执行)
    @EventListener
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleAfterCommit(OrderCreatedEvent event) {
        // 事务提交后才执行,确保数据一致性
    }
}

四、@TransactionalEventListener

java
// 事务事件监听器:确保事件处理在事务提交/回滚后执行

@Component
public class OrderTransactionalListener {

    // AFTER_COMMIT:事务提交后执行(默认,最常用)
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleAfterCommit(OrderCreatedEvent event) {
        // 发送 MQ 消息、短信通知
        // 此时数据已持久化,不会丢消息
    }

    // AFTER_ROLLBACK:事务回滚后执行
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleAfterRollback(OrderCreatedEvent event) {
        // 记录回滚日志,补偿操作
    }

    // BEFORE_COMMIT:事务提交前执行
    @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
    public void handleBeforeCommit(OrderCreatedEvent event) {
        // 提交前最后检查
    }

    // 错误处理
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT,
                                fallbackExecution = true)  // 无事务时也执行
    public void handleWithFallback(OrderCreatedEvent event) { }
}

五、Spring 内置事件

事件发布时间用途
ContextRefreshedEvent容器刷新完成启动后初始化
ContextStartedEvent容器启动启动后任务
ContextStoppedEvent容器停止清理资源
ContextClosedEvent容器关闭最终清理
RequestHandledEvent请求处理完成请求统计
java
@Component
public class StartupListener {

    @EventListener(ContextRefreshedEvent.class)
    public void onApplicationReady(ContextRefreshedEvent event) {
        System.out.println("应用启动完成,执行初始化任务...");
        // 缓存预热
        // 数据初始化
    }
}

六、事件机制底层原理

Spring 事件机制的核心类:

  ApplicationEventMulticaster(事件广播器)
    ├─ SimpleApplicationEventMulticaster(默认实现)
    │   ├─ 同步执行(默认):逐个调用所有监听器
    │   └─ 异步执行(配置 TaskExecutor 后):线程池并发执行
    └─ 自定义实现

  publishEvent() 流程:
    ① 调用 ApplicationEventMulticaster.multicastEvent()
    ② 根据事件类型找到所有匹配的监听器
    ③ 遍历监听器,逐个调用 onApplicationEvent()
    ④ 如果是 @EventListener,通过反射调用标注的方法

七、面试要点

Q1:Spring 事件机制的原理是什么?

  基于观察者模式。ApplicationEventPublisher 发布事件,ApplicationEventMulticaster 广播事件给所有匹配的监听器。默认同步执行,配置 TaskExecutor 后可以异步执行。

Q2:@EventListener 和 @TransactionalEventListener 的区别?

  @EventListener 在事件发布时立即执行(可能在事务中),@TransactionalEventListener 可以指定在事务提交后(AFTER_COMMIT)或回滚后(AFTER_ROLLBACK)执行。发 MQ 消息推荐用 @TransactionalEventListener(AFTER_COMMIT),保证数据已持久化。

Q3:Spring 事件和 MQ 的区别?

  Spring 事件是进程内通信,简单、同步、不持久化;MQ 是跨进程通信,支持持久化、异步、消息回溯。Spring 事件适合单机解耦,MQ 适合分布式解耦。