SpringBoot 事件发布机制:原理与应用

别再在 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() 解耦所有下游逻辑。

记住三个关键决策点:

  1. 要不要等事务提交? → 要就用 @TransactionalEventListener(AFTER_COMMIT)
  2. 要不要异步? → 要就加 @Async + 自定义线程池
  3. 要不要跨进程? → 要就上 MQ

文章结束,喜欢就给个一键三连吧,你的肯定是我最大的动力,点赞上一千我就是脑瘫也出下章

相关推荐
BerrySen1781 小时前
迈向 Next-Gen Java:企业级高并发架构演进与大模型 Agent 落地深度实战
java·开发语言·架构
technology_x2 小时前
2026年财务报表分析软件测评:兼容与安全解析
java·服务器·前端
兰令水2 小时前
hot100【acm版】【2026.7.25/26打卡-java版本】
java·算法·排序算法
Nebula_g2 小时前
Java实现本地Socket通信(三)
java·开发语言·学习·socket·可视化
一水2 小时前
java运行排错,新码旧jar
java·开发语言·jar
wuqingshun3141592 小时前
JAVA中的注解原理是什么?
java
Python+993 小时前
Java 编程语言入门指南
java·开发语言
huahailing10244 小时前
Spring Boot 集成 XXL-Job 完整实现方案(支持动态CRUD)
java·spring boot·后端
薛定谔的猫-菜鸟程序员4 小时前
基于 Electron 的本地短视频解析与下载工具:架构设计与工程实践
java·electron·音视频