spring事务失效的几种场景
spring事务失效的几种场景
- 同一个类中普通方法调用事务方法,事务方法异常不会回滚
- 同一个类中事务A方法调用事务B方法,B方法事务不会回滚
- 同一个类中事务方法调用普通方法,普通方法异常不会回滚
总结:在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务
原因
声明式事务基于Spring AOP实现,将具体业务逻辑与事务处理解耦,在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题
spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction,我们看到的现象就是@Transactional注解无效
既然如此,有人可能会疑惑,第二种情况(同一个类中事务A方法调用事务B方法,B方法事务不会回滚)A方法已经加了@Transactional注解,所以是由代理类来调用的,为什么B事务不会回滚
没错,A方法加了@Transactional注解,所以A方法是由代理类来调用,但是:spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,也就是说,A方法经过代理类调用会有事务生效(原始对象调用A方法前后加了事务),但是调用B方法时是由原始对象调用的而不是代理类(B方法不是外部调用),而原始对象在去调用方法时,是不会再触发代理了(没有经过代理即没有加上事务),所以B方法事务不生效
解决方法
-
将事务方法放到另一个类中进行调用,即符合了在对象之间调用的条件
-
springboot开始aop事务,在启动类application上添加注解@EnableAspectJAutoProxy(exposeProxy = true),通过代理对象调用事务方法:
// 获取代理对象,将代理对象转成目标对象再调用事务方法
((目标对象)AopContext.currentProxy()).事务方法;
- 用@Autowired 注入自己 然后在用注入的bean调用自己的方法也可以
spring官方文档代理参考资料:https://docs.spring.io/spring-framework/docs/3.2.4.RELEASE/spring-framework-reference/html/aop.html#aop-proxying