Spring事务失效的根本原因是代理机制被破坏,核心排查路径是:先确认方法是否被代理(仅public、非final/static、由Spring管理)、调用是否经过代理(禁用this调用、多线程或@Async会丢失上下文)、异常是否触发回滚(默认只响应RuntimeException,需rollbackFor显式配置)、以及环境配置是否到位(事务管理器、InnoDB引擎等)。

事务失效不是“加了注解就一定生效”,而是依赖代理机制、异常传播、方法可见性等一整套协同条件。排查核心思路是:先确认是否走到了 Spring 代理,再检查事务是否被正确触发和回滚。
看方法有没有被代理到
Spring 事务基于 AOP 动态代理,只有被代理对象调用的方法,@Transactional 才会起作用。常见破坏代理的写法:
- 方法是 private / protected / default(包级) —— Spring 源码中明确跳过非 public 方法,直接返回 null,事务属性丢失
- 方法被 final 或 static 修饰 —— CGLIB 无法重写 final 方法,JDK 代理无法代理 static 方法
- 类没被 Spring 管理 —— 比如 new UserService() 手动实例化,完全绕过容器,代理根本不存在
- @Transactional 写在 接口方法上,但项目用了 CGLIB 代理(默认无接口时)—— 接口上的注解不会被子类继承,代理拿不到事务配置
查调用路径有没有绕过代理
即使方法是 public 且被代理,调用方式不对照样失效:
- 同类内部调用(this.methodB())—— this 是原生对象,不是代理对象,事务拦截器压根不执行
- 多线程调用(比如 new Thread(...).start())—— 事务绑定在当前线程的 TransactionSynchronizationManager 上,子线程没有上下文,事务丢失
- 异步方法(@Async)—— 默认新开线程,同样脱离主线程事务上下文
解决方向很明确:拆到不同 Bean 中调用;或用 AopContext.currentProxy() 强制走代理(需开启 expose-proxy=true);或注入自身 Bean 实现间接调用。
立即学习“Java免费学习笔记(深入)”;
盯住异常是不是被“吃掉”或“认不出”
事务回滚不是靠“有异常”就自动发生,而是靠 Spring 的事务拦截器识别后主动回滚:
- try-catch 后没抛出 —— 拦截器看到方法正常返回,认为执行成功,直接提交
- 抛的是受检异常(IOException、SQLException 等) —— 默认只对 RuntimeException 和 Error 回滚,其他异常被忽略
- rollbackFor 配错了类型 —— 比如写了 rollbackFor = IOException.class,但实际抛的是 SQLException
稳妥做法是统一加 @Transactional(rollbackFor = Exception.class),避免漏判;若必须捕获,就在 catch 块里加 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。
验环境和配置是否兜底到位
再正确的代码,缺了基础支撑也白搭:
- 没加 @EnableTransactionManagement 或对应 XML 配置 —— 事务增强器根本没注册
- 数据源没配 PlatformTransactionManager —— 注解找不到管理器,静默失效
- 数据库引擎不支持事务 —— 比如 MySQL 用 MyISAM 表,commit/rollback 全无效
- 传播行为设成 NOT_SUPPORTED 或 NEVER —— 明确告诉 Spring “别开事务”
建议上线前核对 application.yml 中是否声明了 transaction-manager,并用 show create table 确认表引擎是 InnoDB。


















