@Transactional 自调用失效问题解析
一、背景
”脏脏包“在技术群里问了一个问题:”大家有在项目中遇到这样的场景吗 在一个service层重写的方法中调用一个私有方法。 service重写的方法不加事务 私有方法想加入事务 他去调用私有方法时 私有方法需要被事务控制“ 。
这个问题比较典型,面试时也经常被问到,在此简单整理一下。
二、Spring注解方式的事务实现机制
在应用调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器(图 2 有相关介绍)AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务, 如图 1 所示。
图 1. Spring 事务实现机制
Spring AOP 代理有 CglibAopProxy 和 JdkDynamicAopProxy 两种,图 1 是以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。
对于 JdkDynamicAopProxy,需要调用其 invoke 方法。
三、分析
3.1 为什么@Transactional 只能应用到 public 方法才有效?
这是因为在使用 Spring AOP 代理时,Spring 在调用在的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取 @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。
其源码如下:
[code]private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { // 关键在这里 if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } else { Class<?> userClass = ProxyUtils.getUserClass(targetClass); Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass); specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); TransactionAttribute txAtt = null; if (specificMethod != method) { txAtt = this.findTransactionAttribute(method); if (txAtt != null) { return txAtt; } txAtt = this.findTransactionAttribute(method.getDeclaringClass()); if (txAtt != null || !this.enableDefaultTransactions) { return txAtt; } } txAtt = this.findTransactionAttribute(specificMethod); if (txAtt != null) { return txAtt; } else { txAtt = this.findTransactionAttribute(specificMethod.getDeclaringClass()); if (txAtt != null) { return txAtt; } else if (!this.enableDefaultTransactions) { return null; } else { Method targetClassMethod = this.repositoryInformation.getTargetClassMethod(method); if (targetClassMethod.equals(method)) { return null; } else { txAtt = this.findTransactionAttribute(targetClassMethod); if (txAtt != null) { return txAtt; } else { txAtt = this.findTransactionAttribute(targetClassMethod.getDeclaringClass()); return txAtt != null ? txAtt : null; } } } } } }
非公有函数事务属性信息返回null
3.2 为什么自调用无效?
在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。
若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。
[code]@Service public class OrderService { private void insert() { insertOrder(); } @Transactional public void insertOrder() { //SQL操作 } }
insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。
四、解决方法
4.1 可以自注入
4.2 可以使用上下文工具类获取当前对象的代理类
4.3 maven中加入spring-aspects 和 aspectjrt 的依赖以及 aspectj-maven-plugin插件
注:
第二节和第三节部分内容转载自:
透彻的掌握 Spring 中@transactional 的使用
创作不易,如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。
- springboot @Transactional 自调用失效问题
- 遇到问题-----IE中js调用getjson二次点击失效
- C++调用python时 debug链接错误python_d.lib无法解析的问题
- Grails笔记二:grails 2.4.3版本下generate-*失效问题解析
- 解决ViewPager和PagerAdapter中调用notifyDataSetChanged失效的问题
- Pyscripter 不能正确调用另一文件中模块的问题的解析(Internal Engine 和 Remote Engine)
- ejb客户端的三种调用方法,以及InitialContext lookup后的jndi对象在服务重启后缓存失效的问题
- scala调用java带有可变参数的函数传数组解析错误问题解决办法
- 在QQ群里有人提问有没有C语言的XML解析,偶然想到了这个问题:C++调用C库,简单试验:
- 有关Viewpager 在pageAdapter中调用notifyDataSetChanged失效的问题
- Spring 从同一个类中的某个方法调用另一个有注解(@Transactional)的方法时,失效的解决方案
- 关于.net里调用外部CSS失效的问题解决
- asp.net 调用asp的css样式失效问题
- 关于.net里调用外部CSS失效的问题解决
- JSON格式自动解析遇到的调用方法问题.fromJson() ..readValue()
- spring 声明式事务、异步调用、AOP灯增强类功能 失效问题
- spring @transactional public和自调用方法的问题处理
- 关于调用skin++会出现无法解析的外部符号问题解决方案
- Spring Cacheable标签 内部调用失效问题
- fileupload插件调用upload.parseRequest(request)解析得到空值问题