DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Spring 事务事件没有执行,检查异步 multicaster 与发布事务

核对事务是否存在、监听阶段和注册线程;区分6.1前异步multicaster路径与6.1标准分发约定,保留无事务事件语义。

普通 @EventListener 能收到事件,换成 @TransactionalEventListener 后没有执行;项目又给 SimpleApplicationEventMulticaster 配了异步执行器。先核对事件发布时是否真的存在事务,以及事务监听器的注册有没有被移到另一个线程。

对于 PlatformTransactionManager 管理的普通线程事务,事务上下文与发布线程相关。Spring Framework 6.1 以前,标准 multicaster 配了执行器就会异步调用监听器,事务监听器可能因此在没有原事务的线程上尝试注册。Framework 6.1 起,标准实现会尊重监听器的 supportsAsyncExecution();事务监听器返回 false,注册过程应留在原发布线程。

因此,不能把旧版的这条路径当成所有当前版本的默认行为。先查实际运行的 Framework 版本、multicaster Bean 和分发实现,自定义代码也要检查是否遵守这个约定。

先区分没有注册和还没到执行阶段

@TransactionalEventListener 默认阶段是 AFTER_COMMIT。事务还没提交时,方法没有执行是正常的;事务最终回滚,也不会触发这个提交后阶段。其他可选阶段包括 BEFORE_COMMIT、AFTER_ROLLBACK 和 AFTER_COMPLETION。

发布处只是写了 @Transactional,不代表运行时已有事务。同类内部调用、错误的事务管理器或事件在事务开始前就发布,都可能让它没有可绑定的事务。可对照注解实际经过代理的条件,先确认入口。

默认 fallbackExecution = false 时,没有事务的事件不会调用这个监听方法。诊断时分别记录发布位置、注册位置的线程,以及发布时的实际事务状态;日志只记录受控业务标识,避免把完整事件或用户数据写出。

如果发布线程有事务,注册线程却换了,继续检查 multicaster。若注册仍在原线程,再看最终提交或回滚、监听阶段、事件类型是否匹配和异常日志。这几条路径需要分开验证。

旧版异步分发要让事务监听器在调用线程注册

在受影响的旧版本中,可以先恢复事务监听器的同步注册路径,或者按项目依赖管理升级到兼容版本。撤下全局异步执行器可能改变普通监听器的执行时延与异常反馈,应评估影响,保留原配置后在隔离环境验证。

Framework 6.1 的标准 SimpleApplicationEventMulticaster 会检查监听器是否允许异步执行。若升级后仍发生线程切换,核对实际加载的依赖,确认使用的确实是这段实现;自行编写的 multicaster 不会因为接口新增了方法就自动遵守它。

这里的同步要求针对事务监听器绑定到发布事务的过程。监听器后续的业务处理若又用了 @Async,那是另一个执行边界,它的事务、异常与重试仍需单独设计。不要手工把事务的 ThreadLocal 或数据库连接复制到线程池,让两个线程共享调用方事务。

fallbackExecution 改的是无事务时的语义

设置 fallbackExecution = true 会允许没有事务时直接处理事件。业务若明确支持这种执行方式,可以采用;它不能保证“提交后才发送通知”,也不能补回丢失的原事务。

已经进入 AFTER_COMMIT 的处理,如果还要新增数据库写入,应设计真实独立的写事务,不能只因为线程上还能看到某些资源,就假定新写入会随已经结束的事务提交。

响应式应用还要单独看 ReactiveTransactionManager。Framework 6.1 起支持相应事务事件,但事务上下文来自 Reactor context,需按接口约定放进发布事件的 source;不能套用普通线程事务的 ThreadLocal 判断。

调整后,分别验证成功提交、回滚、无事务发布及普通非事务监听器。确认事件只在预期阶段执行,线程与异常处理符合业务要求。若发布重复或需要失败重试,仍要保留事件处理的幂等约束;补回监听器执行不应导致重复业务操作。