Spring 事件机制(ApplicationEvent/Listener/Publisher)源码级
问题
Spring 的事件发布-监听机制是如何实现的?基于什么模式?
面试回答
核心答案
Spring 事件机制基于观察者模式,核心组件有三个:
- ApplicationEvent——事件体,继承自
EventObject,封装事件源和携带的数据。 - ApplicationListener——监听器接口,回调方法
onApplicationEvent(event),接收特定类型的事件。 - ApplicationEventPublisher——事件发布器,
ApplicationContext默认实现了该接口,任何 Spring 管理的 bean 都可以注入它来发布事件。
事件发布流程(简化版):
publishEvent(event)
→ 获取 ApplicationEventMulticaster(默认 SimpleApplicationEventMulticaster)
→ 遍历所有匹配的 ApplicationListener
→ 调用 invokeListener() 执行 onApplicationEvent 回调Spring 支持同步和异步两种模式:默认是同步的——发布线程会阻塞直到所有监听器执行完毕;通过 @Async 注解可以将监听器改为异步执行。
代码示例
// 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():
// 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 方法会遍历所有注册的监听器,逐一调用:
// 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() 步骤:
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_ROLLBACK、AFTER_COMPLETION、BEFORE_COMMIT 四个阶段:
@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,在事务提交/回滚完成后回调,触发监听器执行。核心在 TransactionalEventListenerFactory 和 ApplicationListenerMethodTransactionalAdapter。
典型场景:业务方法执行成功后发送 MQ 消息。如果不用事务事件,方法执行完直接发 MQ,但事务随后回滚了,MQ 消息就变成了"脏消息"。用 @TransactionalEventListener(phase = AFTER_COMMIT) 可以保证事务提交后消息才发出。
2. 同步事件发布的异常风险
问题:如果监听器抛出异常,发布者会怎样?
默认同步模式下,监听器抛出的异常会直接穿透到 publishEvent() 方法,导致发布者后续逻辑无法执行。比如:
@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:设置
SimpleApplicationEventMulticaster的ErrorHandler,这样可以捕获异常但不中断执行 - 方案 C:使用
@Async异步化监听器,异常不会影响发布线程
3. 事件发布顺序控制
多个监听器监听同一事件时,执行顺序由 @Order 注解控制:
@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 包装任意对象:
// 发布泛型事件
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 源码深度解析》