Spring 事件机制 - ApplicationEvent、@EventListener 与事件驱动架构
概述
Spring 事件机制(Spring Event Mechanism)是 Spring Framework 核心容器(Spring Core)提供的一套基于观察者模式(Observer Pattern)的事件发布-监听基础设施。它允许应用内部各个 Bean 之间通过事件驱动的方式解耦通信,是构建松耦合、可扩展应用的基石。
与 Spring 的依赖注入(DI)和面向切面编程(AOP)并列,事件机制是 Spring 容器的"三大扩展点"之一,广泛应用于业务解耦、异步处理、事务边界控制等场景。
核心角色
| 角色 | 说明 | 对应接口/注解 |
|---|---|---|
| 事件(Event) | 描述"发生了什么"的数据对象 | ApplicationEvent / PayloadApplicationEvent |
| 发布者(Publisher) | 发布事件的一方 | ApplicationEventPublisher |
| 监听器(Listener) | 响应事件的一方 | ApplicationListener / @EventListener |
| 广播器(Multicaster) | 将事件分发给所有匹配的监听器 | ApplicationEventMulticaster |
1. Spring 事件体系
Spring 事件体系的核心接口和类定义在 spring-context 模块中,它们共同构成了事件驱动的基础设施。
1.1 ApplicationEvent —— 事件的基类
ApplicationEvent 是 Spring 中所有应用事件的抽象基类,继承自 java.util.EventObject。
// 源码位置:org.springframework.context.ApplicationEvent
public abstract class ApplicationEvent extends EventObject {
/** 事件发生的时间戳 */
private final long timestamp;
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
public final long getTimestamp() {
return this.timestamp;
}
}关键要点:
source:事件源对象(通过EventObject继承),通常是发布事件的 Bean 或相关业务对象。timestamp:事件创建时的时间戳,记录事件发生的时刻。
Spring 5.x 开始,ApplicationEvent 不再强制要求序列化,移除了 implements Serializable。
1.2 ApplicationListener —— 监听器接口
ApplicationListener<E extends ApplicationEvent> 是一个泛型接口,定义了一个方法 onApplicationEvent(E event)。
// 源码位置:org.springframework.context.ApplicationListener
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> {
void onApplicationEvent(E event);
// Spring 5.3 新增:根据事件类型进行匹配的便捷方法
// 默认实现基于泛型参数 E 的类型
static <T> ApplicationListener<?> forPayload(Consumer<T> consumer) {
// ...
}
}它是传统的函数式接口,可以在 Java 8+ 中以 Lambda 形式使用。
1.3 ApplicationEventPublisher —— 事件发布器
ApplicationEventPublisher 是事件发布的入口接口,注入它即可在任何 Bean 中发布事件。
// 源码位置:org.springframework.context.ApplicationEventPublisher
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}Spring 会自动检测 ApplicationContext(它实现了 ApplicationEventPublisher)并将其注入到需要的地方。
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// 业务逻辑...
publisher.publishEvent(new OrderCreatedEvent(this, order));
}
}1.4 ApplicationEventMulticaster —— 事件广播器
事件发布的核心委派对象,AbstractApplicationContext 将 publishEvent() 的调用委托给 ApplicationEventMulticaster。
// 源码位置:org.springframework.context.event.ApplicationEventMulticaster
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void removeApplicationListener(ApplicationListener<?> listener);
void removeAllListeners();
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}默认实现是 SimpleApplicationEventMulticaster,详见第 6 节的源码分析。
2. 自定义事件
Spring 提供了多种方式来定义自定义事件,开发者可以根据需求灵活选择。
2.1 继承 ApplicationEvent(传统方式)
最直接的方式:创建事件类继承 ApplicationEvent。
// 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
private final Long orderId;
private final String userId;
private final BigDecimal amount;
public OrderCreatedEvent(Object source, Long orderId, String userId, BigDecimal amount) {
super(source);
this.orderId = orderId;
this.userId = userId;
this.amount = amount;
}
// getter 方法...
}// 发布事件
@Component
public class OrderEventPublisher {
@Autowired
private ApplicationEventPublisher publisher;
public void publishOrderCreated(Order order) {
publisher.publishEvent(new OrderCreatedEvent(this, order.getId(),
order.getUserId(), order.getAmount()));
}
}// 监听事件
@Component
public class OrderEventListener implements ApplicationListener<OrderCreatedEvent> {
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
System.out.println("收到订单创建事件:订单ID = " + event.getOrderId());
}
}缺点:事件类与 Spring 框架耦合(继承 ApplicationEvent)。
2.2 使用 PayloadApplicationEvent(Spring 4.2+)
Spring 4.2 引入了 PayloadApplicationEvent,允许将任意 POJO 作为事件负载发布,无需自定义事件类。
// 定义事件数据(纯 POJO,不依赖 Spring)
public class OrderCreatedPayload {
private Long orderId;
private String userId;
private BigDecimal amount;
// 构造器、getter/setter...
}// 发布事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// 业务逻辑...
publisher.publishEvent(new OrderCreatedPayload(order.getId(),
order.getUserId(), order.getAmount()));
}
}// 监听事件(通过 @EventListener 类型推断)
@Component
public class OrderEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedPayload payload) {
System.out.println("收到订单创建事件:订单ID = " + payload.getOrderId());
}
}Spring 在发布 POJO 对象时,会自动将其包装为 PayloadApplicationEvent;在 @EventListener 解析时,会提取负载类型进行匹配。
2.3 使用 GenericApplicationEvent(Spring 5.3+)
Spring 5.3 引入了 GenericApplicationEvent,支持更灵活的类型传递和解析。
// 发布时直接传入泛型事件
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void createOrder(Order order) {
// Spring 内部会包装为 GenericApplicationEvent 或 PayloadApplicationEvent
publisher.publishEvent(order);
}
}GenericApplicationEvent 在内部通过 ResolvableType 对泛型类型进行解析,使监听器能精确匹配泛型参数。
3. @EventListener 注解使用
Spring 4.2 引入了 @EventListener 注解,声明式地将任意方法标记为事件监听器,极大简化了事件监听代码。
3.1 基本用法
@Component
public class OrderEventListeners {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("收到订单创建事件:" + event.getOrderId());
}
}方法参数类型决定了它监听哪种事件。Spring 通过 ResolvableTypeProvider 或方法签名推断事件类型。
3.2 条件过滤(condition)
@EventListener 的 condition 属性支持 SpEL 表达式,在事件分发时动态判断是否执行监听器。
@EventListener(condition = "#event.amount.compareTo(T(java.math.BigDecimal).valueOf(1000)) > 0")
public void handleLargeOrder(OrderCreatedEvent event) {
System.out.println("大额订单通知:" + event.getOrderId() + ",金额:" + event.getAmount());
}SpEL 上下文绑定:
| 变量名 | 含义 |
|---|---|
#root.args | 方法参数数组 |
#root.event | 当前事件对象 |
#事件参数名 | 事件对象本身(如 #event) |
更复杂的条件示例:
@EventListener(condition = "#event.amount > 5000 && #event.userId.startsWith('VIP_')")
public void handleVipLargeOrder(OrderCreatedEvent event) {
System.out.println("VIP大额订单:" + event.getOrderId());
}3.3 泛型事件监听
当事件本身是泛型类型时,@EventListener 支持根据泛型参数进行精确匹配。
// 定义泛型事件
public class BaseEvent<T> extends ApplicationEvent {
private final T data;
public BaseEvent(Object source, T data) {
super(source);
this.data = data;
}
public T getData() {
return data;
}
}// 监听特定泛型类型的事件
@Component
public class GenericEventListeners {
@EventListener
public void handleStringEvent(BaseEvent<String> event) {
System.out.println("收到 String 类型事件:" + event.getData());
}
@EventListener
public void handleOrderEvent(BaseEvent<Order> event) {
System.out.println("收到 Order 类型事件:" + event.getData().getId());
}
}注意:泛型事件匹配依赖
ResolvableType机制,确保在发布时使用ResolvableType包装或通过GenericApplicationEvent携带泛型信息。
3.4 返回新事件(事件链)
@EventListener 方法可以返回一个新事件,Spring 会自动将返回值作为新事件发布,形成事件链。
@EventListener
public OrderPaidEvent handleOrderCreated(OrderCreatedEvent event) {
System.out.println("订单创建,触发支付流程");
return new OrderPaidEvent(this, event.getOrderId());
}
@EventListener
public void handleOrderPaid(OrderPaidEvent event) {
System.out.println("订单已支付:" + event.getOrderId());
}也可以返回集合,批量发布多个事件:
@EventListener
public List<ApplicationEvent> handleOrderCreated(OrderCreatedEvent event) {
return Arrays.asList(
new OrderPaidEvent(this, event.getOrderId()),
new NotificationEvent(this, "订单已创建")
);
}3.5 监听多个事件类型
Spring 5.3.5+ 支持在 @EventListener 中使用 classes 属性监听多种事件类型:
@EventListener({OrderCreatedEvent.class, OrderPaidEvent.class})
public void handleOrderEvents(Object event) {
if (event instanceof OrderCreatedEvent) {
System.out.println("订单创建");
} else if (event instanceof OrderPaidEvent) {
System.out.println("订单支付");
}
}4. @TransactionalEventListener 事务事件
@TransactionalEventListener 是 @EventListener 的装饰器,在 Spring 4.2 中引入,用于在事务的特定阶段触发事件监听器。这是 Spring 事件机制中最具实战价值的功能之一。
4.1 基本用法
@Component
public class OrderTransactionListeners {
@TransactionalEventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// 默认在事务提交后执行
System.out.println("事务已提交,处理订单:" + event.getOrderId());
}
}要求:
@TransactionalEventListener必须在活跃的事务上下文中才会生效,Spring 会注册一个内部的TransactionSynchronization来延迟执行。
4.2 TransactionPhase —— 4 种触发时机
@TransactionalEventListener 的 phase 属性控制监听器在事务生命周期的哪个阶段触发,定义在 TransactionPhase 枚举中。
| 枚举值 | 触发时机 | 典型场景 |
|---|---|---|
BEFORE_COMMIT | 事务提交之前 | 事务内预校验、资源预分配 |
AFTER_COMMIT(默认) | 事务成功提交之后 | 发消息、发邮件、写审计日志 |
AFTER_ROLLBACK | 事务回滚之后 | 回滚补偿、告警通知 |
AFTER_COMPLETION | 事务完成之后(提交或回滚均触发) | 资源清理、统计计数 |
使用示例:
@Component
public class TransactionalEventListeners {
// 提交前:校验库存
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void checkStock(OrderCreatedEvent event) {
System.out.println("[BEFORE_COMMIT] 校验库存 - 订单:" + event.getOrderId());
// 仍处于事务中,可以通过数据库检查库存
}
// 提交后:发通知(默认 phase)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendNotification(OrderCreatedEvent event) {
System.out.println("[AFTER_COMMIT] 发送订单通知 - 订单:" + event.getOrderId());
}
// 回滚后:补偿处理
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleRollback(OrderCreatedEvent event) {
System.out.println("[AFTER_ROLLBACK] 订单回滚补偿 - 订单:" + event.getOrderId());
}
// 完成后:资源清理
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void cleanup(OrderCreatedEvent event) {
System.out.println("[AFTER_COMPLETION] 清理临时资源 - 订单:" + event.getOrderId());
}
}4.3 fallbackExecution 属性
默认情况下,如果没有活跃的事务,@TransactionalEventListener 不会执行。设置 fallbackExecution = true 可以在无事务时也执行监听器。
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
public void handleWithFallback(OrderCreatedEvent event) {
System.out.println("无论是否有事务都会执行,有事务则在提交后执行");
}4.4 源码实现原理
@TransactionalEventListener 的核心处理逻辑在 TransactionalEventListenerFactory 和 ApplicationListenerMethodTransactionalAdapter 中。
简化流程:
TransactionalEventListenerFactory检测方法上是否有@TransactionalEventListener注解- 将方法包装为
ApplicationListenerMethodTransactionalAdapter - 该适配器内部的
processEvent()方法会:- 检查当前是否存在事务
- 如果存在,通过
TransactionSynchronizationManager.registerSynchronization()注册一个TransactionSynchronization - 根据
phase在同步回调的不同方法中执行监听逻辑 - 如果不存在事务且
fallbackExecution = false,则静默跳过
核心伪代码逻辑:
// TransactionSynchronization 注册逻辑示意
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronization() {
@Override
public void beforeCommit(boolean readOnly) {
if (phase == BEFORE_COMMIT) invokeListener(event);
}
@Override
public void afterCommit() {
if (phase == AFTER_COMMIT) invokeListener(event);
}
@Override
public void afterCompletion(int status) {
if (phase == AFTER_COMPLETION) invokeListener(event);
if (phase == AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) invokeListener(event);
}
}
);
} else if (fallbackExecution) {
invokeListener(event);
}
}5. 异步事件
默认情况下,事件监听器是同步执行的——发布线程会阻塞直到所有监听器执行完毕。对于耗时操作(发送邮件、生成报表等),Spring 提供了两种异步方案。
5.1 @Async + @EventListener 组合
最简单的方式:在监听器方法上同时标注 @EventListener 和 @Async。
@Component
@EnableAsync // 在配置类上开启异步支持
public class AsyncEventListeners {
@Async
@EventListener
public void sendEmail(OrderCreatedEvent event) {
System.out.println("[异步] 发送邮件 - 订单:" + event.getOrderId()
+ ",当前线程:" + Thread.currentThread().getName());
}
@Async
@EventListener
public void sendSms(OrderCreatedEvent event) {
System.out.println("[异步] 发送短信 - 订单:" + event.getOrderId()
+ ",当前线程:" + Thread.currentThread().getName());
}
}注意事项:
@Async需要配合@EnableAsync使用- 默认使用
SimpleAsyncTaskExecutor(每次创建新线程,生产环境建议替换为线程池) - 异步监听器不参与发布者的事务(因为在新线程中执行,事务上下文不会传播)
- 异步监听器的异常不会影响发布者
配置自定义线程池:
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("event-async-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}5.2 AsyncApplicationEventMulticaster
另一种方式:替换 Spring 默认的事件广播器为 SimpleApplicationEventMulticaster 并配置自定义 Executor。
@Configuration
public class EventConfig {
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
// 设置自定义线程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("event-multicaster-");
executor.initialize();
multicaster.setTaskExecutor(executor);
return multicaster;
}
}两种方式对比:
| 方案 | 粒度 | 配置复杂度 | 灵活性 |
|---|---|---|---|
@Async + @EventListener | 方法级别(细粒度) | 中等(需 @EnableAsync) | 高,可混合同步/异步 |
AsyncApplicationEventMulticaster | 全局级别(粗粒度) | 简单 | 低,所有事件均异步 |
最佳实践:大部分场景推荐
@Async + @EventListener组合,可以精确控制哪些监听器异步执行。
5.3 异步事件的事务问题
当异步事件与 @TransactionalEventListener 结合时,需要特别注意:异步线程中没有原始事务上下文。
// ⚠️ 这种组合需要小心
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommitAsync(OrderCreatedEvent event) {
// 此时在异步线程中执行,原始事务已经提交
// 如果有数据库操作,需要开启新事务
}通常的做法是:在异步监听器中通过 @Transactional(propagation = Propagation.REQUIRES_NEW) 开启独立事务。
6. 事件发布源码:SimpleApplicationEventMulticaster 完整精读
SimpleApplicationEventMulticaster 是 Spring 默认的事件广播器,也是理解 Spring 事件分发机制的核心入口。以下基于 Spring Framework 5.3.x 源码进行分析。
6.1 类继承关系
ApplicationEventMulticaster(接口)
↑
AbstractApplicationEventMulticaster(抽象类)
↑
SimpleApplicationEventMulticaster(具体实现)AbstractApplicationEventMulticaster 负责监听器的注册和管理(存储、检索、匹配),SimpleApplicationEventMulticaster 负责事件的分发执行。
6.2 multicastEvent() 入口方法
// 源码位置:org.springframework.context.event.SimpleApplicationEventMulticaster
@Override
public void multicastEvent(ApplicationEvent event) {
// 推断事件类型:从 event 中获取 ResolvableType,用于精确匹配泛型事件
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
// 获取事件对应的 ResolvableType(用于泛型匹配)
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取当前线程的 TaskExecutor(如果有设置)
Executor executor = getTaskExecutor();
// 核心:获取所有匹配该事件的监听器
// getApplicationListeners() 定义在 AbstractApplicationEventMulticaster 中
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 如果设置了 Executor → 异步执行
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
// 否则 → 同步执行
else {
invokeListener(listener, event);
}
}
}执行流程总结:
publishEvent()
↓
AbstractApplicationContext.publishEvent()
↓
ApplicationEventMulticaster.multicastEvent()
↓
遍历匹配的监听器
├── 有 Executor → 异步执行
└── 无 Executor → 同步执行6.3 resolveDefaultEventType() —— 事件类型推断
// 源码位置:SimpleApplicationEventMulticaster
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
// 如果事件实现了 ResolvableTypeProvider,则通过它获取精确的类型信息
// 这对于泛型事件的支持至关重要
if (event instanceof ResolvableTypeProvider) {
return ((ResolvableTypeProvider) event).getResolvableType();
}
// 否则直接使用事件的 Class 类型
return ResolvableType.forInstance(event);
}PayloadApplicationEvent 和 GenericApplicationEvent 都实现了 ResolvableTypeProvider,使得泛型类型信息不会被擦除。
6.4 getApplicationListeners() —— 监听器检索与匹配
该方法定义在 AbstractApplicationEventMulticaster 中,是事件分发的核心检索逻辑。
// 源码位置:org.springframework.context.event.AbstractApplicationEventMulticaster
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
// 获取事件源类型(发布事件的 Bean 类型)
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
// 从缓存中查找(使用 ListenerCacheKey 作为键)
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
if (retriever != null) {
// 缓存命中,直接返回
return retriever.getApplicationListeners();
}
// 缓存未命中,执行全量检索
// ...(省略锁和双重检查逻辑)
return retrieveApplicationListeners(eventType, sourceType, retriever);
}检索匹配逻辑(supportsEvent()):
// AbstractApplicationEventMulticaster.supportsEvent()
private boolean supportsEvent(ApplicationListener<?> listener,
ResolvableType eventType, Class<?> sourceType) {
// 对于实现了 ApplicationListener 的监听器,检查泛型参数是否匹配
if (listener instanceof GenericApplicationListener) {
return ((GenericApplicationListener) listener).supportsEventType(eventType);
}
// 对于 ApplicationListenerMethodAdapter(@EventListener 编译后),检查类型
// ...
return true;
}6.5 invokeListener() —— 监听器执行
// 源码位置:SimpleApplicationEventMulticaster
private void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
try {
// 实际调用监听器的 onApplicationEvent 方法
doInvokeListener(listener, event);
} catch (Throwable ex) {
// 默认情况下,异常不会影响其他监听器的执行
// 通过 ErrorHandler 可以自定义异常处理
if (this.errorHandler != null) {
this.errorHandler.handleError(ex);
} else {
logger.error("....", ex); // 仅记录日志
}
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 直接调用监听器的 onApplicationEvent 方法
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
// 类型转换异常时的优雅降级(极少发生)
// ...
}
}6.6 异常处理策略
SimpleApplicationEventMulticaster 支持通过 setErrorHandler() 设置全局异常处理器:
@Configuration
public class EventConfig {
@Bean
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
// 设置全局异常处理器
multicaster.setErrorHandler(throwable -> {
System.err.println("事件处理异常:" + throwable.getMessage());
// 可以集成告警系统、监控埋点等
});
return multicaster;
}
}6.7 整体源码流程总结
publishEvent(event)
│
▼
AbstractApplicationContext.publishEvent()
│ ┌─ 将 POJO 包装为 PayloadApplicationEvent
│ └─ 获取 ApplicationEventMulticaster
│
▼
SimpleApplicationEventMulticaster.multicastEvent(event, eventType)
│
├─ 1. 解析事件类型(ResolvableType)
│
├─ 2. 获取匹配的监听器列表
│ └─ AbstractApplicationEventMulticaster.getApplicationListeners()
│ ├─ 从缓存获取(ListenerCacheKey)
│ └─ 未命中 → 遍历所有监听器,逐个匹配类型
│
└─ 3. 遍历分发
├─ 有 Executor → executor.execute(() -> invokeListener())
└─ 无 Executor → invokeListener()
│
▼
doInvokeListener(listener, event)
│
└─ listener.onApplicationEvent(event)
│
├─ ApplicationListenerMethodAdapter(@EventListener 编译后)
└─ 原始 ApplicationListener 实现7. 事件监听器的执行顺序
当针对同一事件有多个监听器时,可以通过 @Order 注解(或实现 Ordered 接口)控制执行顺序。
7.1 @Order 注解控制顺序
@Component
public class OrderedEventListeners {
@Order(1) // 数字越小,优先级越高(越先执行)
@EventListener
public void firstHandler(OrderCreatedEvent event) {
System.out.println("1st: 更新订单状态");
}
@Order(2)
@EventListener
public void secondHandler(OrderCreatedEvent event) {
System.out.println("2nd: 发送订单通知");
}
@Order(3)
@EventListener
public void thirdHandler(OrderCreatedEvent event) {
System.out.println("3rd: 记录审计日志");
}
}对于实现了 ApplicationListener 接口的监听器,可以实现 Ordered 接口:
@Component
public class AuditListener implements ApplicationListener<OrderCreatedEvent>, Ordered {
@Override
public int getOrder() {
return 100; // 数字越小越先执行
}
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
System.out.println("审计日志记录");
}
}7.2 排序原理
排序逻辑在 AbstractApplicationEventMulticaster 的监听器检索阶段完成:
// AbstractApplicationEventMulticaster.retrieveApplicationListeners()
protected Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever) {
List<ApplicationListener<?>> listeners = new ArrayList<>();
// ...(遍历所有监听器,匹配类型)
// 排序:按 @Order 注解值或 Ordered.getOrder() 升序排列
AnnotationAwareOrderComparator.sort(listeners);
return listeners;
}7.3 注意事项
@Order值越小,优先级越高,执行越靠前- 默认情况下,同一事件的多个监听器同步执行,顺序有保证
- 如果某些监听器标记了
@Async,它们会被提交到线程池,执行顺序不可控 - 建议将同步监听器用于顺序敏感的逻辑,异步监听器用于可并行的非关键任务
8. 实战案例:用户注册事件驱动
本节演示一个完整的用户注册事件驱动链路:用户注册完成后,依次触发发送欢迎邮件、赠送积分、发放优惠券等操作,各步骤通过事件机制解耦。
8.1 架构设计
用户注册请求
│
▼
UserService.register()
│
├─ 保存用户信息到数据库
│
└─ publisher.publishEvent(UserRegisteredEvent)
│
┌───────┼───────┬───────┐
▼ ▼ ▼ ▼
EmailListener PointListener CouponListener (其他)
(发邮件) (送积分) (发优惠券)8.2 事件定义
/**
* 用户注册成功事件
* 继承 ApplicationEvent,携带注册信息
*/
public class UserRegisteredEvent extends ApplicationEvent {
private final Long userId;
private final String username;
private final String email;
private final LocalDateTime registeredAt;
public UserRegisteredEvent(Object source, Long userId, String username,
String email, LocalDateTime registeredAt) {
super(source);
this.userId = userId;
this.username = username;
this.email = email;
this.registeredAt = registeredAt;
}
// getter 方法
public Long getUserId() { return userId; }
public String getUsername() { return username; }
public String getEmail() { return email; }
public LocalDateTime getRegisteredAt() { return registeredAt; }
}8.3 服务层 —— 注册与事件发布
@Service
@Slf4j
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private ApplicationEventPublisher publisher;
@Transactional
public User register(RegisterRequest request) {
log.info("开始用户注册:{}", request.getUsername());
// 1. 保存用户
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setCreatedAt(LocalDateTime.now());
user = userRepository.save(user);
log.info("用户注册成功,用户ID:{}", user.getId());
// 2. 发布注册事件(事务提交后自动触发后续流程)
publisher.publishEvent(new UserRegisteredEvent(
this, user.getId(), user.getUsername(),
user.getEmail(), user.getCreatedAt()));
return user;
}
}8.4 事务事件监听器 —— 确保事务提交后执行
@Component
@Slf4j
public class UserRegistrationEventListeners {
/**
* 发送欢迎邮件(事务提交后执行)
*/
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(1)
public void sendWelcomeEmail(UserRegisteredEvent event) {
log.info("[发邮件] 发送欢迎邮件给用户 {} ({}),线程:{}",
event.getUsername(), event.getEmail(),
Thread.currentThread().getName());
// 模拟发送邮件
emailService.sendWelcomeEmail(event.getEmail(), event.getUsername());
}
/**
* 赠送注册积分(事务提交后执行)
*/
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(2)
public void grantRegistrationPoints(UserRegisteredEvent event) {
log.info("[送积分] 为用户 {} 赠送 100 注册积分,线程:{}",
event.getUsername(), Thread.currentThread().getName());
// 模拟赠送积分
pointService.grantPoints(event.getUserId(), 100, "注册赠送");
}
/**
* 发放新人优惠券(事务提交后执行)
*/
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(3)
public void issueNewUserCoupons(UserRegisteredEvent event) {
log.info("[发券] 为用户 {} 发放新人优惠券,线程:{}",
event.getUsername(), Thread.currentThread().getName());
// 模拟发放优惠券
couponService.issueCoupon(event.getUserId(), "NEW_USER_50", "新人50元券");
couponService.issueCoupon(event.getUserId(), "NEW_USER_20", "新人20元券");
}
/**
* 注册失败后的补偿处理(事务回滚后执行)
*/
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
@Order(99)
public void handleRegistrationFailure(UserRegisteredEvent event) {
log.warn("[补偿] 用户 {} 注册事务回滚,进行补偿处理", event.getUsername());
// 清理已分配的临时资源等
}
}8.5 异步配置
@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("user-event-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
log.error("异步事件处理异常 - 方法:{},参数:{}",
method.getName(), Arrays.toString(params), ex);
};
}
}8.6 控制器层
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public ResponseEntity<ApiResponse<UserVO>> register(@Valid @RequestBody RegisterRequest request) {
User user = userService.register(request);
return ResponseEntity.ok(ApiResponse.success("注册成功", UserVO.from(user)));
}
}8.7 事件驱动的优势
在这个案例中,事件驱动架构带来了以下好处:
| 特性 | 说明 |
|---|---|
| 解耦 | UserService 不需要知道邮件、积分、优惠券等下游逻辑 |
| 可扩展 | 新增功能(如注册后推送新手指南)只需添加新的 @EventListener 方法,无需修改现有代码 |
| 事务安全 | 通过 @TransactionalEventListener(AFTER_COMMIT) 确保事务提交后才执行后续操作 |
| 异步化 | @Async 使邮件发送、积分赠送等操作异步执行,不阻塞用户注册响应 |
| 容错 | 各监听器独立运行,一个监听器失败不影响其他监听器 |
| 可观测 | 可以在监听器中统一埋点,监控整个事件链路 |
8.8 测试验证
@SpringBootTest
class UserRegistrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Test
void testUserRegistrationEventDriven() {
// 准备
RegisterRequest request = new RegisterRequest();
request.setUsername("test_user");
request.setEmail("test@example.com");
request.setPassword("password123");
// 执行
User user = userService.register(request);
// 验证
assertNotNull(user.getId());
Optional<User> saved = userRepository.findById(user.getId());
assertTrue(saved.isPresent());
assertEquals("test_user", saved.get().getUsername());
// 验证异步事件已触发(通过日志或 Mockito verify 等待异步完成)
// verify(emailService, timeout(5000)).sendWelcomeEmail(anyString(), anyString());
// verify(pointService, timeout(5000)).grantPoints(anyLong(), anyInt(), anyString());
// verify(couponService, timeout(5000)).issueCoupon(anyLong(), anyString(), anyString());
}
}总结
Spring 事件机制是 Spring Framework 提供的一套轻量级、容器内嵌的事件驱动解决方案,具有以下特点:
- 开箱即用:无需额外引入中间件,Spring Context 内置完整的事件基础设施
- 声明式编程:
@EventListener注解使得事件监听极度简洁 - 事务感知:
@TransactionalEventListener提供了与事务生命周期精确对齐的触发能力 - 异步支持:可以灵活地在全局或方法级别配置异步执行
- 类型安全:泛型事件 +
ResolvableType提供了编译期类型检查保障 - 低入侵:POJO 事件体不依赖 Spring API,便于测试和复用
适用场景
- 同一 JVM 内的模块解耦(如注册→发邮件→送积分→发券)
- 事务边界后的异步处理(
@TransactionalEventListener(AFTER_COMMIT)+@Async) - 插件化扩展(通过事件监听器机制动态添加扩展点)
- 审计日志、操作记录等横切关注点
与消息中间件的对比
| 能力 | Spring Event | 消息中间件 (RocketMQ/Kafka) |
|---|---|---|
| 进程内 | ✅ 原生支持 | ❌ 需要远程调用 |
| 跨进程 | ❌ 不支持 | ✅ 核心能力 |
| 持久化 | ❌ 不支持 | ✅ 支持持久化 |
| 重试机制 | ❌ 无(需自行实现) | ✅ 支持重试 |
| 消息回溯 | ❌ 不支持 | ✅ 支持 |
| 复杂度 | ⭐ 低 | ⭐⭐⭐ 高 |
最佳实践:进程内解耦优先使用 Spring Event;需要跨服务、持久化、可靠投递时选择消息中间件。
参考版本:本文源码分析基于 Spring Framework 5.3.x