spring事务失效的几种场景以及原因

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring事务失效的几种场景以及原因相关的知识,希望对你有一定的参考价值。

参考技术A

spring事务失效场景可能大家在很多文章都看过了,所以今天就水一篇,看大家能不能收获一些不一样的东西。直接进入主题

失效原因: spring事务生效的前提是,service必须是一个bean对象
解决方案: 将service注入spring

失效原因: spring默认只会回滚非检查异常和error异常
解决方案: 配置rollbackFor

失效原因: spring事务只有捕捉到了业务抛出去的异常,才能进行后续的处理,如果业务自己捕获了异常,则事务无法感知
解决方案:
1、将异常原样抛出;
2、设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

失效原因: spring事务切面的优先级顺序最低,但如果自定义的切面优先级和他一样,且自定义的切面没有正确处理异常,则会同业务自己捕获异常的那种场景一样
解决方案:
1、在切面中将异常原样抛出;
2、在切面中设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

失效原因: spring事务默认生效的方法权限都必须为public

解决方案:
1、将方法改为public;
2、修改TansactionAttributeSource,将publicMethodsOnly改为false【这个从源码跟踪得出结论】
3、开启 AspectJ 代理模式【从spring文档得出结论】

具体步骤:

1、在pom引入aspectjrt坐标以及相应插件

2、在启动类上加上如下配置

注: 如果是在idea上运行,则需做如下配置


4、直接用TransactionTemplate

示例:

失效原因: 子容器扫描范围过大,将未加事务配置的serivce扫描进来

解决方案:
1、父子容器个扫个的范围;
2、不用父子容器,所有bean都交给同一容器管理

注: 因为示例是使用springboot,而springboot启动默认没有父子容器,只有一个容器,因此就该场景就演示示例了

失效原因: 因为spring事务是用动态代理实现,因此如果方法使用了final修饰,则代理类无法对目标方法进行重写,植入事务功能

解决方案:
1、方法不要用final修饰

失效原因: 原因和final一样

解决方案:
1、方法不要用static修饰

失效原因: 本类方法不经过代理,无法进行增强

解决方案:
1、注入自己来调用;
2、使用@EnableAspectJAutoProxy(exposeProxy = true) + AopContext.currentProxy()

失效原因: 因为spring的事务是通过数据库连接来实现,而数据库连接spring是放在threadLocal里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的,即是属于不同事务

失效原因: 使用的传播特性不支持事务

失效原因: 使用了不支持事务的存储引擎。比如mysql中的MyISAM

注: 因为springboot,他默认已经开启事务管理器。org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration。因此示例略过

失效原因: 当代理类的实例化早于AbstractAutoProxyCreator后置处理器,就无法被AbstractAutoProxyCreator后置处理器增强

本文列举了14种spring事务失效的场景,其实这14种里面有很多都是归根结底都是属于同一类问题引起,比如因为动态代理原因、方法限定符原因、异常类型原因等

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-transaction-invalid-case

Spring 事务失效的几种场景

1.抛出检查异常导致事务不能正确回滚

spring 默认只会回滚非检查异常,
需要配置rollbackFor属性指定回滚

2.业务方法内自己try-cache异常导致事务不能正确回滚

事务通知只有捕获到目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
解决:cache中原样抛出异常
或者cache代码块中执行 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

3.日志Aop切面 中捕获了service中的异常

设置日志切面优先级大于事务切面

4.事务方法没有使用public方法修饰

改为public
或者修改注解方法 将方法为public设置为false

5.父子容器导致的事务失效

子容器扫描范围过大,把未加事务配置的service扫描进来
解决:web扫controller,spring 扫service ,因为sping中的service才加入 了声明式事务

springboot中只有一个容器,不会有这个问题,以前springmvc整合spring时有父子容器存在这个问题。

6.调用本类方法导致传播行为失效

本类方法调用不经过代理,因此无法增强
解决:依赖注入自己代理来调用
通过AopContext拿到代理对象来调用
通过CTW,LTW实现功能增强

7.Transactional 方法导致的synchronized失效

synchronized保证的仅是目标方法的原子性,环绕目标方法的还有commit等操作,它们并未处于sync块内
解决:synchronized范围扩大至代理方法调用

以上是关于spring事务失效的几种场景以及原因的主要内容,如果未能解决你的问题,请参考以下文章

Spring 事务失效的几种场景

Spring 事务失效的几种场景

面试官:你知道哪些事务失效的场景?

项目中Spring事务失效的场景问题排查

Spring事务失效的原因

259期面试官:Spring事务失效的场景有哪些?如何解决?