为什么 @Transactional 会失效?一次 Debug 看懂 Spring 踩过的所有坑
2026/6/8 21:44:20 网站建设 项目流程

为什么 @Transactional 会失效?一次 Debug 看懂 Spring 踩过的所有坑

上篇我们通过 Debug 看懂了@Transactional为什么能生效。

文章发出去后,评论区很快出现了一个问题:

明明加了 @Transactional,为什么事务还是失效了?

很多人第一反应是:

  • Spring Bug
  • 配置写错了
  • 数据源有问题

但 Debug 一圈源码后会发现,大部分事务失效场景其实都能归结为两类:

没有走代理 或者 事务拦截器没有触发回滚规则

今天先看最常见的几种。

第一坑:同类方法调用(this)

很多人都写过这样的代码:

@ServicepublicclassUserService{publicvoidcreateOrder(){saveOrder();}@TransactionalpublicvoidsaveOrder(){inti=1/0;}}

看起来没毛病。

很多人觉得:

createOrder() ↓ saveOrder() ↓ 事务开启

实际上不是。

Debug 进去会发现:

saveOrder();

编译后其实就是:

this.saveOrder();

调用链如下:

createOrder() ↓ this.saveOrder() ↓ 目标对象

而正常事务应该是:

代理对象 ↓ TransactionInterceptor ↓ 目标方法

这里直接绕过代理对象,事务拦截器根本没执行。

所以事务失效的根本原因不是同类调用,而是:

没有经过代理对象

【如图】

// 不走代理 this.saveOrder() ↓ Target
// 走代理 proxy.saveOrder() ↓ TransactionInterceptor ↓ Target

第二坑:private 方法

很多人会这样写:

@TransactionalprivatevoidsaveOrder(){}

启动没有报错。

调用也正常。

但事务就是不生效。

为什么?因为 Spring AOP 的核心是代理。

以 CGLIB 为例,本质是创建子类:

classUserService$$SpringCGLIBextendsUserService{@OverridepublicvoidsaveOrder(){}}

但 private 方法根本不能被子类重写。

既然无法重写:

无法增强 ↓ 无法织入事务 ↓ 事务失效

【如图】

private ↓ 不能 override ↓ 不能代理 ↓ 事务失效

第三坑:final 方法

再看这个例子:

@TransactionalpublicfinalvoidsaveOrder(){}

很多人觉得:

public 不是能代理吗?

问题出在:

final

CGLIB 依赖继承实现增强。

而 final 方法禁止重写。

例如:

classA{publicfinalvoidsave(){}}

子类:

classBextendsA{@Overridepublicvoidsave(){}}

直接编译失败。

所以结果和 private 一样:

无法重写 ↓ 无法增强 ↓ 事务失效

【图3】

final ↓ 不能 override ↓ 事务失效

第四坑:对象不是 Spring 管理的

这是线上非常常见的问题。

例如:

UserServiceuserService=newUserService();userService.save();

很多新人觉得:

我加了@Transactional 为什么不生效?

原因很简单。

Spring 事务依赖代理对象。

而你自己创建的是:

newUserService()

根本不是容器里的对象。

打印一下:

System.out.println(userService.getClass());

结果:

classcom.demo.UserService

如果是 Spring 容器中的 Bean:

System.out.println(applicationContext.getBean(UserService.class).getClass());

你会看到:

classcom.demo.UserService$$SpringCGLIB$$0

区别就在这里。

一个是原对象。

一个是代理对象。

事务只存在于代理对象中。

【图4】

new UserService() ↓ 原对象 ↓ 无事务
Spring Bean ↓ 代理对象 ↓ TransactionInterceptor ↓ 事务生效

总结

很多事务失效问题看起来五花八门,但本质其实很简单。

第一类:

this调用 private方法 final方法 自己new对象

共同特点:

没有经过代理对象

第二类:

try-catch 异常类型错误 多线程 事务传播

共同特点:

事务执行了 但没有触发回滚规则

这部分下一篇继续讲。


最后留个问题。

下面这段代码会回滚吗?

@Transactionalpublicvoidsave(){try{inti=1/0;}catch(Exceptione){e.printStackTrace();}}

很多人第一反应:

抛异常了 肯定回滚

但真实结果可能和你想的不一样。

评论区猜猜。

下一篇:

《为什么抛了异常,事务还是没回滚?一次 Debug 看懂 Spring 的回滚规则》

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询