别再在 Service 里写一堆 if-else 调用了,Spring 早就给你准备好了优雅解耦的利器。
目录
- 一、从一个真实场景说起
- 二、核心三件套
- 三、源码深度剖析
- [3.1 publishEvent:从一行代码到事件广播](#3.1 publishEvent:从一行代码到事件广播)
- [3.2 监听器注册:容器启动时发生了什么](#3.2 监听器注册:容器启动时发生了什么)
- [3.3 @EventListener 的扫描:EventListenerMethodProcessor](#3.3 @EventListener 的扫描:EventListenerMethodProcessor)
- [3.4 SimpleApplicationEventMulticaster:事件分发的核心引擎](#3.4 SimpleApplicationEventMulticaster:事件分发的核心引擎)
- [3.5 事件匹配算法:getApplicationListeners](#3.5 事件匹配算法:getApplicationListeners)
- [3.6 @TransactionalEventListener 的实现原理](#3.6 @TransactionalEventListener 的实现原理)
- [3.7 @Async 的代理链路](#3.7 @Async 的代理链路)
- [3.8 整体流程图](#3.8 整体流程图)
- [3.9 PayloadApplicationEvent 细节](#3.9 PayloadApplicationEvent 细节)
- 四、四种监听方式对比
- [五、@TransactionalEventListener 详解](#五、@TransactionalEventListener 详解)
- [六、异步事件:@Async 的正确姿势](#六、异步事件:@Async 的正确姿势)
- [七、完整实战 Demo](#七、完整实战 Demo)
- 八、踩坑总结
- [九、Spring Event vs MQ,怎么选?](#九、Spring Event vs MQ,怎么选?)
- [十、进阶:自定义 EventMulticaster](#十、进阶:自定义 EventMulticaster)
- 十一、一句话总结
一、从一个真实场景说起
假设你负责一个电商下单模块,产品经理说:
"用户下单成功后,要发短信通知、加会员积分、推送 APP 消息、扣减库存、记录操作日志。"
最直觉的写法:
java
@Service
public class OrderService {
@Autowired private SmsService smsService;
@Autowired private PointService pointService;
@Autowired private PushService pushService;
@Autowired private StockService stockService;
@Autowired private LogService logService;
public void createOrder(OrderDTO dto) {
// 1. 核心逻辑:创建订单
Order order = buildOrder(dto);
orderMapper.insert(order);
// 2. 一堆"顺便做的事"
smsService.send(order.getPhone(), "下单成功");
pointService.add(order.getUserId(), order.getAmount());
pushService.notify(order.getUserId(), "订单已创建");
stockService.decrease(order.getSkuId(), order.getQuantity());
logService.record("CREATE_ORDER", order.getId());
}
}
问题一目了然:
- OrderService 依赖了 5 个不相关的 Service,职责严重膨胀
- 任何一个下游抛异常,都会影响下单主流程
- 新增一个"下单后发优惠券"的需求,又要改 OrderService
- 想改成异步?每个调用都要包一层线程池,丑到哭
事件机制的核心思想:我只管"喊一嗓子",谁爱听谁听,听完了干嘛跟我没关系。
二、核心三件套
Spring 的事件机制由三个核心组件构成:
| 组件 | 角色 | 类比 |
|---|---|---|
ApplicationEvent |
事件本身(数据载体) | 广播的内容 |
ApplicationEventPublisher |
事件发布者 | 广播电台 |
ApplicationListener |
事件监听者 | 收音机 |
2.1 定义事件
java
public class OrderCreatedEvent extends ApplicationEvent {
private final Long orderId;
private final Long userId;
private final BigDecimal amount;
public OrderCreatedEvent(Object source, Long orderId, Long userId, BigDecimal amount) {
super(source);
this.orderId = orderId;
this.userId = userId;
this.amount = amount;
}
// getter 省略
}
Spring 4.2+ 的简化写法: 事件类不需要继承
ApplicationEvent,任意 POJO 都行:
java
// 对,就这么简单,一个普通类
public class OrderCreatedEvent {
private final Long orderId;
private final Long userId;
private final BigDecimal amount;
public OrderCreatedEvent(Long orderId, Long userId, BigDecimal amount) {
this.orderId = orderId;
this.userId = userId;
this.amount = amount;
}
// getter 省略
}
2.2 发布事件
java
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void createEvent(OrderDTO dto) {
Order order = buildOrder(dto);
orderMapper.insert(order);
// 一行搞定,不管谁在听
eventPublisher.publishEvent(new OrderCreatedEvent(
order.getId(), order.getUserId(), order.getAmount()
));
}
}
2.3 监听事件
java
@Component
public class SmsListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
// 发短信
smsService.send(event.getUserId(), "您的订单 " + event.getOrderId() + " 已创建");
}
}
@Component
public class PointListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
// 加积分
pointService.add(event.getUserId(), event.getAmount());
}
}
重构后的 OrderService 只依赖一个 ApplicationEventPublisher,干净利落。
三、源码深度剖析
以下源码基于 Spring Framework 6.x(SpringBoot 3.x),核心逻辑在 5.x 中一致。
3.1 publishEvent:从一行代码到事件广播
你在业务代码里写的 eventPublisher.publishEvent(event),这个 eventPublisher 是谁?
答案:AbstractApplicationContext 本身。它实现了 ApplicationEventPublisher 接口。
java
// AbstractApplicationContext.java(完整调用链)
protected void publishEvent(Object event, @Nullable ResolvableType typeHint) {
Assert.notNull(event, "Event must not be null");
// ========== 第一步:事件包装 ==========
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent ae) {
// 已经是 ApplicationEvent,直接用
applicationEvent = ae;
} else {
// 普通 POJO → 包装成 PayloadApplicationEvent
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (typeHint == null) {
typeHint = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// ========== 第二步:早期事件处理 ==========
// 如果 Multicaster 还没初始化(容器启动早期),先缓存到 earlyApplicationEvents
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
// ========== 第三步:正式广播 ==========
getApplicationEventMulticaster().multicastEvent(applicationEvent, typeHint);
}
// ========== 第四步:父子容器传播 ==========
// 如果当前容器有父容器,事件会向上传播(子容器的事件,父容器也能收到)
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext abstractApplicationContext) {
abstractApplicationContext.publishEvent(event, typeHint);
} else {
this.parent.publishEvent(event);
}
}
}
关键细节:
- 你发布的任何 POJO 对象,都会被包装成
PayloadApplicationEvent<T>,泛型 T 就是你对象的类型 earlyApplicationEvents机制保证了容器还没完全启动时发布的事件不会丢失,等registerListeners()完成后统一补发- 父子容器传播是递归的,多层嵌套容器会一路向上传
3.2 监听器注册:容器启动时发生了什么?
AbstractApplicationContext.refresh() 是 Spring 容器启动的核心模板方法,事件相关的注册发生在第 8 步:
java
// AbstractApplicationContext.refresh()(精简)
public void refresh() throws BeansException {
// ... 前面 7 步省略(prepareRefresh, obtainBeanFactory, ...)
// 第 8 步:注册监听器
registerListeners();
// ... 后面步骤省略(finishBeanFactoryInitialization, finishRefresh)
}
java
// AbstractApplicationContext.java
protected void registerListeners() {
// ===== 1. 注册"硬编码"监听器 =====
// 通过 context.addApplicationListener() 手动添加的
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// ===== 2. 注册 Bean 形式的监听器(按名字注册,延迟获取实例)=====
// 实现了 ApplicationListener 接口的 Bean
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// ===== 3. 补发早期事件 =====
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null; // 置空,后续事件走正常广播
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
注意: 这里只注册了实现 ApplicationListener 接口的 Bean。那 @EventListener 注解的方法呢?
3.3 @EventListener 的扫描:EventListenerMethodProcessor
@EventListener 的处理由 EventListenerMethodProcessor 完成,它是一个 SmartInitializingSingleton + BeanFactoryPostProcessor:
java
// EventListenerMethodProcessor.java(核心逻辑)
public class EventListenerMethodProcessor
implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
@Override
public void afterSingletonsInstantiated() {
// 所有单例 Bean 初始化完成后触发
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
String[] beanNames = beanFactory.getBeanNamesForType(Object.class, false, false);
for (String beanName : beanNames) {
// 获取 Bean 的真实类型(处理代理、FactoryBean 等)
Class<?> type = beanFactory.getType(beanName);
if (type != null) {
processBean(beanName, type);
}
}
}
private void processBean(final String beanName, final Class<?> targetType) {
// 扫描类上所有带 @EventListener 注解的方法
Map<Method, EventListener> annotatedMethods =
MethodIntrospector.selectMethods(targetType,
(method) -> AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
for (Method method : annotatedMethods.keySet()) {
// 将每个 @EventListener 方法包装成一个 ApplicationListener
EventListener ann = annotatedMethods.get(method);
// 解析方法参数,确定监听的事件类型
ResolvableType declaredEventType = resolveDeclaredEventType(method);
// 创建适配器
ApplicationListener<?> applicationListener =
new ApplicationListenerMethodAdapter(beanName, targetType, method);
// 注册到 Multicaster
context.addApplicationListener(applicationListener);
}
}
}
ApplicationListenerMethodAdapter 是核心适配器 ,它把"一个普通方法"适配成了 ApplicationListener 接口:
java
// ApplicationListenerMethodAdapter.java(精简)
public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
private final String beanName;
private final Method method;
private final ResolvableType declaredEventType; // 方法参数类型 = 监听的事件类型
private final String condition; // @EventListener(condition="...")
@Override
public void onApplicationEvent(ApplicationEvent event) {
processEvent(event);
}
public void processEvent(ApplicationEvent event) {
// 1. 解析事件参数(从 PayloadApplicationEvent 中提取 payload)
Object[] args = resolveArguments(event);
// 2. 条件判断(SpEL)
if (shouldHandle(event, args)) {
// 3. 反射调用目标方法
Object result = doInvoke(args);
// 4. 如果方法有返回值,把返回值作为新事件发布出去
if (result != null) {
publishEvent(result);
}
}
}
protected boolean shouldHandle(ApplicationEvent event, Object[] args) {
if (StringUtils.hasText(this.condition)) {
// 构建 SpEL 上下文,#event 指向事件对象,#root.args 指向方法参数
EvaluationContext evaluationContext = createEvaluationContext(event, args);
return evaluator.condition(this.condition, this.methodKey, evaluationContext);
}
return true;
}
}
这就是为什么 @EventListener 方法有返回值时会自动发布新事件的源码依据。
3.4 SimpleApplicationEventMulticaster:事件分发的核心引擎
这是整个事件机制最核心的类,负责"把事件送达每一个匹配的监听器":
java
// SimpleApplicationEventMulticaster.java
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
@Nullable
private Executor taskExecutor; // 默认 null → 同步
@Nullable
private ErrorHandler errorHandler;
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
@Override
public void multicastEvent(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);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
} catch (Throwable err) {
errorHandler.handleError(err); // 有 ErrorHandler 则吞掉异常
}
} else {
doInvokeListener(listener, event); // 无 ErrorHandler 则异常直接抛出
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
listener.onApplicationEvent(event); // 最终调用
}
}
3.5 事件匹配算法:getApplicationListeners
getApplicationListeners(event, type) 不是每次都全量扫描,它有缓存机制:
java
// AbstractApplicationEventMulticaster.java
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
// 构建缓存 Key:事件类型 + 源类型
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
// 先查缓存
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
if (existingRetriever != null) {
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
}
// 缓存未命中,执行匹配
return retrieveApplicationListeners(eventType, sourceType, retriever);
}
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, Class<?> sourceType, CachedListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
// 合并两个来源:直接注册的 + 按 BeanName 注册的
Set<ApplicationListener<?>> filteredListeners = new LinkedHashSet<>();
Set<String> filteredListenerBeans = new LinkedHashSet<>();
// 遍历所有已注册的监听器
for (ApplicationListener<?> listener : this.defaultRetriever.applicationListeners) {
if (supportsEvent(listener, eventType, sourceType)) {
filteredListeners.add(listener);
}
}
// 遍历按 BeanName 注册的监听器(延迟加载)
for (String listenerBeanName : this.defaultRetriever.applicationListenerBeans) {
ApplicationListener<?> listener = getBean(listenerBeanName, ApplicationListener.class);
if (supportsEvent(listener, eventType, sourceType)) {
filteredListeners.add(listener);
}
}
// 按 @Order / Ordered 接口排序
AnnotationAwareOrderComparator.sort(filteredListeners);
return filteredListeners;
}
supportsEvent 的匹配逻辑:
java
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
// 1. 如果是 GenericApplicationListener,直接问它
if (listener instanceof GenericApplicationListener gal) {
return gal.supportsEventType(eventType);
}
// 2. 对于 ApplicationListenerMethodAdapter,检查方法参数类型
// 是否兼容当前事件类型(支持泛型、继承)
ResolvableType declaredEventType = getResolvableType(listener);
if (declaredEventType == null) {
return false;
}
// 3. 类型兼容性判断:
// 监听 ApplicationEvent → 匹配所有事件
// 监听 OrderCreatedEvent → 只匹配 OrderCreatedEvent 及其子类
// 监听 PayloadApplicationEvent<OrderCreatedEvent> → 匹配泛型参数
if (declaredEventType.isAssignableFrom(eventType)) {
return true;
}
// 4. 泛型解析:PayloadApplicationEvent<T> 中的 T
if (declaredEventType.hasGenerics() && eventType.hasGenerics()) {
return declaredEventType.isAssignableFrom(eventType);
}
return false;
}
3.6 @TransactionalEventListener 的实现原理
@TransactionalEventListener 并不是在事件发布时立即执行,而是注册了一个事务同步回调。
处理它的适配器是 TransactionalApplicationListenerMethodAdapter(继承自 ApplicationListenerMethodAdapter):
java
// TransactionalApplicationListenerMethodAdapter.java(核心逻辑)
public class TransactionalApplicationListenerMethodAdapter
extends ApplicationListenerMethodAdapter {
private final TransactionPhase phase; // BEFORE_COMMIT / AFTER_COMMIT / ...
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 不是直接执行!而是注册事务同步回调
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 有活跃事务 → 注册回调
TransactionSynchronization synchronization =
new TransactionSynchronization() {
@Override
public void beforeCommit(boolean readOnly) {
if (phase == TransactionPhase.BEFORE_COMMIT) {
processEvent(event); // 提交前执行
}
}
@Override
public void afterCommit() {
if (phase == TransactionPhase.AFTER_COMMIT) {
processEvent(event); // 提交后执行
}
}
@Override
public void afterCompletion(int status) {
if (phase == TransactionPhase.AFTER_ROLLBACK
&& status == STATUS_ROLLED_BACK) {
processEvent(event); // 回滚后执行
}
if (phase == TransactionPhase.AFTER_COMPLETION) {
processEvent(event); // 完成后执行(不管成功失败)
}
}
};
TransactionSynchronizationManager.registerSynchronization(synchronization);
} else {
// 没有活跃事务
if (fallbackExecution) {
processEvent(event); // fallbackExecution=true 时直接执行
}
// 否则静默丢弃,不执行
}
}
}
底层依赖的是 Spring 的 TransactionSynchronizationManager:
事务开始
│
├─ TransactionSynchronizationManager.initSynchronization()
│ → 在当前线程的 ThreadLocal 中创建同步回调列表
│
├─ 业务代码执行
│ ├─ publishEvent() → 监听器被调用
│ │ └─ 不执行业务逻辑,而是 registerSynchronization(callback)
│ │ → 把回调加入 ThreadLocal 列表
│
├─ 事务提交
│ └─ AbstractPlatformTransactionManager.triggerAfterCommit()
│ └─ 遍历所有 TransactionSynchronization
│ └─ synchronization.afterCommit() ← 此时才真正执行监听逻辑
│
└─ 清理 ThreadLocal
这就解释了为什么 @TransactionalEventListener(AFTER_COMMIT) 中的代码拿不到事务上下文------事务已经提交完毕了。
3.7 @Async 的代理链路:事件监听器是怎么变异步的?
当你在 @EventListener 方法上加 @Async 时,涉及两层代理:
第一层:EventListenerMethodProcessor
→ 把 @EventListener 方法包装成 ApplicationListenerMethodAdapter
→ 注册到 Multicaster
第二层:AsyncAnnotationBeanPostProcessor
→ 发现 Bean 中有 @Async 方法
→ 为该 Bean 创建代理(JDK 动态代理或 CGLIB)
→ 代理中插入 AsyncExecutionInterceptor
调用链变成:
Multicaster.invokeListener(listener, event)
└─ listener.onApplicationEvent(event)
└─ 实际调用的是代理对象的方法
└─ AsyncExecutionInterceptor.invoke()
├─ 1. 获取线程池(@Async("eventExecutor") 指定的)
├─ 2. 把实际方法调用包装成 Callable
├─ 3. 提交到线程池:executor.submit(callable)
└─ 4. 立即返回(不等待结果)
java
// AsyncExecutionInterceptor.java(精简)
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 确定用哪个线程池
Executor executor = determineAsyncExecutor(invocation.getMethod());
// 包装成异步任务
Callable<Object> task = () -> {
try {
Object result = invocation.proceed(); // 执行真实方法
if (result instanceof Future<?> future) {
return future.get();
}
} catch (Throwable ex) {
handleError(ex, invocation.getMethod());
}
return null;
};
// 提交到线程池,立即返回
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
}
一个关键问题:@Async 和 @TransactionalEventListener 同时使用时,谁先谁后?
答案是:@TransactionalEventListener 的适配器先拦截(决定"什么时候执行"),然后执行时走 @Async 代理(决定"在哪个线程执行")。
事件发布
└─ TransactionalApplicationListenerMethodAdapter.onApplicationEvent()
└─ 注册 TransactionSynchronization 回调
└─ 事务提交后触发 afterCommit()
└─ processEvent() → 调用目标 Bean 的方法
└─ 经过 @Async 代理 → 提交到线程池异步执行
3.8 整体流程图

3.9 一个容易忽略的细节:PayloadApplicationEvent
当你发布一个普通 POJO 时:
java
publisher.publishEvent(new OrderCreatedEvent(...));
Spring 内部会包装:
java
new PayloadApplicationEvent<>(source, payload)
这个包装类的 getResolvableType() 返回的是 PayloadApplicationEvent<OrderCreatedEvent>,而不是简单的 ApplicationEvent。
这就是为什么 @EventListener 方法的参数类型能精确匹配到 OrderCreatedEvent------匹配算法会解析泛型参数 T,而不是只看外层 ApplicationEvent。
java
// PayloadApplicationEvent.java
public class PayloadApplicationEvent<T> extends ApplicationEvent
implements ResolvableTypeProvider {
private final T payload;
@Override
public ResolvableType getResolvableType() {
// 返回 PayloadApplicationEvent<T> 的完整泛型信息
return ResolvableType.forClassWithGenerics(getClass(),
ResolvableType.forInstance(this.payload));
}
}
四、四种监听方式对比
4.1 实现 ApplicationListener 接口
java
@Component
public class OrderListener implements ApplicationListener<OrderCreatedEvent> {
@Override
public void onApplicationEvent(OrderCreatedEvent event) {
// 处理逻辑
}
}
- 优点:类型安全,编译期就能确定事件类型
- 缺点:一个类只能监听一种事件
4.2 @EventListener 注解(推荐)
java
@Component
public class OrderListener {
@EventListener
public void handleCreated(OrderCreatedEvent event) {
// 处理下单
}
@EventListener
public void handlePaid(OrderPaidEvent event) {
// 处理支付,同一个类可以监听多种事件
}
}
4.3 条件过滤
java
@EventListener(condition = "#event.amount > 1000")
public void handleVipOrder(OrderCreatedEvent event) {
// 只处理金额大于 1000 的订单
}
SpEL 可用变量:#event(事件对象)、#root.args
4.4 @TransactionalEventListener(事务绑定,重点!)
java
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(OrderCreatedEvent event) {
// 只有事务提交成功后才执行
}
五、@TransactionalEventListener 详解
5.1 为什么需要它?
经典 Bug 场景:
java
@Transactional
public void createOrder(OrderDTO dto) {
orderMapper.insert(order);
eventPublisher.publishEvent(new OrderCreatedEvent(...));
// 此时事务还没提交!
}
@EventListener // 同步监听,在事务提交前就执行了
public void onOrderCreated(OrderCreatedEvent event) {
// 这里去查订单 → 可能查不到(事务未提交,其他连接看不到)
// 这里发短信 → 如果后续事务回滚了,短信白发
}
5.2 四个 Phase
java
public enum TransactionPhase {
BEFORE_COMMIT, // 事务提交前(默认)
AFTER_COMMIT, // 事务提交后(最常用)
AFTER_ROLLBACK, // 事务回滚后
AFTER_COMPLETION // 事务完成后(不管成功失败)
}
5.3 使用示例
java
@Component
public class OrderNotificationListener {
// 事务提交成功后才发短信,避免回滚后白发
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendSmsAfterCommit(OrderCreatedEvent event) {
smsService.send(event.getUserId(), "下单成功");
}
// 事务回滚后做补偿
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void compensateOnRollback(OrderCreatedEvent event) {
log.warn("订单创建失败,执行补偿: orderId={}", event.getOrderId());
}
}
5.4 注意事项
| 坑点 | 说明 |
|---|---|
| 没有事务时不执行 | 如果发布事件的方法没有 @Transactional,@TransactionalEventListener 默认不会触发 |
| fallbackExecution | 设置 fallbackExecution = true 可以在无事务时也执行 |
| AFTER_COMMIT 中抛异常 | 事务已提交,异常不会回滚业务数据,需要自己处理 |
| 不能在 AFTER_COMMIT 中修改同一事务的数据 | 事务已经结束了 |
java
// 无事务时的兜底方案
@TransactionalEventListener(
phase = TransactionPhase.AFTER_COMMIT,
fallbackExecution = true // 没有事务也执行
)
public void handle(OrderCreatedEvent event) {
// ...
}
六、异步事件:@Async 的正确姿势
6.1 基本用法
java
@Configuration
@EnableAsync // 必须开启!
public class AsyncConfig {
@Bean("eventExecutor")
public Executor eventExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("event-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
java
@Component
public class AsyncListeners {
// 指定线程池,别用默认的 SimpleAsyncTaskExecutor(每次新建线程)
@Async("eventExecutor")
@EventListener
public void asyncHandle(OrderCreatedEvent event) {
// 异步执行,不阻塞主流程
pushService.notify(event.getUserId(), "订单已创建");
}
}
6.2 @Async 失效的三大坑
坑一:同类调用
java
@Component
public class OrderService {
public void createOrder(OrderDTO dto) {
// ...
this.asyncHandle(event); // 失效!绕过了代理
}
@Async
@EventListener
public void asyncHandle(OrderCreatedEvent event) {
// 不会异步执行
}
}
原因:@Async 基于 AOP 代理,this.xxx() 是直接调用,不经过代理。
坑二:方法不是 public
java
@Async
@EventListener
private void handle(OrderCreatedEvent event) {
// 静默失效,不报错,但不异步
}
坑三:@TransactionalEventListener + @Async 的事务问题
java
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(OrderCreatedEvent event) {
// 异步线程中没有事务上下文
// 如果这里需要操作数据库,要自己开新事务
}
6.3 异步异常处理
异步方法抛异常,调用方是感知不到的。必须配置异常处理器:
java
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
log.error("异步事件处理异常: method={}, params={}",
method.getName(), Arrays.toString(params), ex);
// 可以接告警、写失败记录表、重试等
};
}
}
七、完整实战 Demo
7.1 事件定义
java
// 订单创建事件
public class OrderCreatedEvent {
private final Long orderId;
private final Long userId;
private final BigDecimal amount;
private final String phone;
public OrderCreatedEvent(Long orderId, Long userId, BigDecimal amount, String phone) {
this.orderId = orderId;
this.userId = userId;
this.amount = amount;
this.phone = phone;
}
// getter...
}
7.2 发布者
java
@Service
public class OrderService {
private final OrderMapper orderMapper;
private final ApplicationEventPublisher publisher;
public OrderService(OrderMapper orderMapper, ApplicationEventPublisher publisher) {
this.orderMapper = orderMapper;
this.publisher = publisher;
}
@Transactional(rollbackFor = Exception.class)
public Long createOrder(CreateOrderRequest request) {
// 核心业务
Order order = new Order();
order.setUserId(request.getUserId());
order.setAmount(request.getAmount());
order.setPhone(request.getPhone());
order.setStatus(OrderStatus.CREATED);
orderMapper.insert(order);
// 发布事件(一行代码,解耦所有下游)
publisher.publishEvent(new OrderCreatedEvent(
order.getId(),
order.getUserId(),
order.getAmount(),
order.getPhone()
));
return order.getId();
}
}
7.3 监听者们
java
// 短信通知 - 事务提交后异步执行
@Component
@Slf4j
public class SmsNotifyListener {
@Async("eventExecutor")
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
log.info("发送短信: phone={}, orderId={}", event.getPhone(), event.getOrderId());
// smsClient.send(event.getPhone(), "下单成功,订单号:" + event.getOrderId());
}
}
// 积分服务 - 事务提交后同步执行(积分很重要,要确保成功)
@Component
@Slf4j
public class PointListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void onOrderCreated(OrderCreatedEvent event) {
int points = event.getAmount().intValue(); // 1元=1积分
log.info("增加积分: userId={}, points={}", event.getUserId(), points);
// pointService.add(event.getUserId(), points);
}
}
// 操作日志 - 同步记录,跟主事务一起
@Component
@Slf4j
public class AuditLogListener {
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
log.info("记录审计日志: orderId={}, userId={}", event.getOrderId(), event.getUserId());
// auditLogMapper.insert(new AuditLog("CREATE_ORDER", event.getOrderId()));
}
}
// VIP 专属处理 - 条件过滤
@Component
@Slf4j
public class VipListener {
@Async("eventExecutor")
@TransactionalEventListener(
phase = TransactionPhase.AFTER_COMMIT,
condition = "#event.amount > 5000"
)
public void onVipOrder(OrderCreatedEvent event) {
log.info("大额订单,触发 VIP 专属服务: orderId={}", event.getOrderId());
// vipService.assignManager(event.getUserId());
}
}
7.4 执行顺序分析
createOrder() 被调用
│
├─ orderMapper.insert(order) ← 数据库操作(事务内)
│
├─ publishEvent(OrderCreatedEvent) ← 发布事件
│ │
│ ├─ AuditLogListener ← @EventListener,同步,立即执行(事务内)
│ │
│ ├─ SmsNotifyListener ← @TransactionalEventListener,注册回调,暂不执行
│ ├─ PointListener ← 同上
│ └─ VipListener ← 同上
│
├─ 事务提交 [OK]
│ │
│ ├─ PointListener.onOrderCreated() ← AFTER_COMMIT,同步执行
│ ├─ SmsNotifyListener.onOrderCreated() ← AFTER_COMMIT + @Async,异步执行
│ └─ VipListener.onVipOrder() ← AFTER_COMMIT + @Async + 条件满足,异步执行
│
└─ 返回 orderId
八、踩坑总结
坑 1:监听器执行顺序
多个 @EventListener 监听同一事件,默认顺序不确定。用 @Order 控制:
java
@EventListener
@Order(1) // 数字越小越先执行
public void first(OrderCreatedEvent event) { }
@EventListener
@Order(2)
public void second(OrderCreatedEvent event) { }
坑 2:同步监听器异常会中断后续监听器
java
@EventListener
@Order(1)
public void first(OrderCreatedEvent event) {
throw new RuntimeException("boom");
}
@EventListener
@Order(2)
public void second(OrderCreatedEvent event) {
// 不会执行!被上面的异常中断了
}
解决方案: 同步监听器内部 try-catch,或者改成异步(异步各自独立线程,互不影响)。
坑 3:事件类被代理后类型丢失
如果事件类被 CGLIB 代理(极少见),instanceof 判断可能出问题。建议事件类用 final 修饰。
坑 4:循环依赖
java
// A 发布事件 → B 监听 → B 又发布事件 → A 监听 → 无限循环
Spring 不会帮你检测循环,自己注意事件流的 DAG(有向无环图)。
坑 5:@EventListener 方法有返回值
java
@EventListener
public OrderPaidEvent onOrderCreated(OrderCreatedEvent event) {
return new OrderPaidEvent(...); // 会自动发布这个新事件!
}
这是 Spring 的特性(不是 Bug):监听方法有返回值时,返回值会作为新事件被发布。不知道这个特性的话,可能引发意外的事件链。
九、Spring Event vs MQ,怎么选?
| 维度 | Spring Event | MQ(RabbitMQ/Kafka) |
|---|---|---|
| 通信范围 | 单 JVM 进程内 | 跨进程、跨服务 |
| 可靠性 | 进程挂了事件就丢了 | 消息持久化,可重试 |
| 性能 | 极快(方法调用级别) | 有网络开销 |
| 事务一致性 | 可绑定本地事务 | 需要分布式事务/最终一致性方案 |
| 复杂度 | 零依赖,开箱即用 | 需要部署维护中间件 |
| 适用场景 | 单体应用内模块解耦 | 微服务间通信、削峰填谷 |
选型建议:
- 单体应用 / 模块内解耦 → Spring Event,简单够用
- 微服务间通信 → MQ,没得选
- 需要消息不丢失 → MQ
- 需要延迟消息、死信队列、消息回溯 → MQ
- 过渡方案: 先用 Spring Event 解耦,后续拆微服务时,只需把 Listener 改成 MQ Consumer,Publisher 改成 MQ Producer,业务代码不用动
十、进阶:自定义 EventMulticaster
如果你想全局控制事件分发行为(比如默认异步、异常兜底),可以自定义:
java
@Configuration
public class EventConfig {
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
// 全局异步(所有事件都异步,慎用)
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(16);
executor.setThreadNamePrefix("global-event-");
executor.initialize();
multicaster.setTaskExecutor(executor);
// 全局异常处理
multicaster.setErrorHandler(t -> {
log.error("事件处理异常", t);
});
return multicaster;
}
}
注意:Bean 名称必须是
applicationEventMulticaster,否则 Spring 不会识别。
十一、总结
Spring Event 的本质就是观察者模式的框架级实现:发布者不关心谁在听,监听者不关心谁在发。一个
publishEvent()解耦所有下游逻辑。
记住三个关键决策点:
- 要不要等事务提交? → 要就用
@TransactionalEventListener(AFTER_COMMIT) - 要不要异步? → 要就加
@Async+ 自定义线程池 - 要不要跨进程? → 要就上 MQ
文章结束,喜欢就给个一键三连吧,你的肯定是我最大的动力,点赞上一千我就是脑瘫也出下章