spring-transaction源码分析(3)Transactional事务失效原因
问题概述
在Transactional方法中使用this方式调用另一个Transactional方法时,拦截器无法拦截到被调用方法,严重时会使事务失效。
类似以下代码:
@Transactional
public void insertBlogList(List<Blog> blogList) {
for (Blog blog : blogList) {
this.blogMapper.insertBlog(blog);
}
try {
TimeUnit.SECONDS.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Transactional
public void deleteBlogByCondition(BlogSearchParameter parameter) {
List<Blog> blogs = this.blogMapper.selectBlogByParameter(parameter);
for (Blog blog : blogs) {
this.blogMapper.deleteBlog(blog.getId());
}
// 抛出一个RuntimeException
throw new RuntimeException("deleteBlogByCondition抛出一个异常");
}
@Transactional
public void insertAndDeleteBlogList2(List<Blog> blogList, BlogSearchParameter parameter) {
// 插入数据
this.insertBlogList(blogList);
// 删除数据
try {
this.deleteBlogByCondition(parameter);
} catch (Exception e) {
System.err.printf("Err:%s%n", e.getMessage());
}
System.out.println("继续插入数据");
// 继续插入数据
this.insertBlogList(blogList);
}
正常情况下,执行到"继续插入数据"时会抛出一个"rollback only"的异常,然后事务回滚。
而现在的现象是:
- 三个操作都不会开启事务,出现异常也不会回滚
- "删除数据"操作会把符合条件的数据都删除掉
- "继续插入数据"操作会再插入数据
原因分析
在EnableTransactionManagement注解mode属性的文档中:
The default is AdviceMode.PROXY. Please note that proxy mode allows for interception of calls through the proxy only.
Local calls within the same class cannot get intercepted that way; an Transactional annotation on such a method within
a local call will be ignored since Spring's interceptor does not even kick in for such a runtime scenario.
For a more advanced mode of interception, consider switching this to AdviceMode.ASPECTJ.
大概意思是:mode属性的默认值是AdviceMode.PROXY,这种方式仅允许通过代理对来调用事务方法,同一个类的本地调用无法被事务切面拦截。如果要解决这个问题,可以使用AdviceMode.ASPECTJ模式。
其实这个问题的根本原因与spring-tx无关,而是spring-aop的实现方式造成的。
从spring-aop拦截器分析问题原因
在DynamicAdvisedInterceptor和JdkDynamicAopProxy中有一段类似的代码:


其中target就是原始的业务层Bean对象。
在后续创建ReflectiveMethodInvocation/CglibMethodInvocation时又将此target传递了进去:
// JDK
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
// Cglib
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
proceed方法中在拦截器链最后会调用目标方法:
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 略
}
protected Object invokeJoinpoint() throws Throwable {
// 反射调用目标方法
// 这个target就是原始Bean对象
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
所以如果在目标方法中使用this方法调用另一个需要被拦截的方法,将不会执行拦截逻辑。
spring-transaction源码分析(3)Transactional事务失效原因的更多相关文章
- spring transaction源码分析--事务架构
1. 引言 事务特性 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性.事 ...
- Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理
AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...
- Spring AMQP 源码分析 02 - CachingConnectionFactory
### 准备 ## 目标 了解 CachingConnectionFactory 在默认缓存模式下的工作原理 ## 前置知识 <Spring AMQP 源码分析 01 - Impatie ...
- 5.2 Spring5源码--Spring AOP源码分析二
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- 5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
- Spring Security 源码分析(四):Spring Social实现微信社交登录
社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...
- spring AOP源码分析(三)
在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- Spring AOP 源码分析 - 筛选合适的通知器
1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...
随机推荐
- C++中数字与字符串之间的转换(转载自http://www.cnblogs.com/luxiaoxun/)
C++中数字与字符串之间的转换 1.字符串数字之间的转换(1)string --> char * string str("OK"); char * p = str ...
- ceph集群搭建详细教程(ceph-deploy)
ceph-deploy比较适合生产环境,不是用cephadm搭建.相对麻烦一些,但是并不难,细节把握好就行,只是命令多一些而已. 实验环境 服务器主机 public网段IP(对外服务) cluster ...
- Docker的安装、镜像加速配置
wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce. ...
- 开发期间flask运行方式(1.x和2.x区别)
1.x使用app.run(参数) 可以使用debug=True控制处于什么模式. app.run(host="127.0.0.1", port=5000, debug=True) ...
- 酷表ChatExcel -北大出品免费自动处理表格工具
酷表ChatExcel是通过文字聊天实现Excel的交互控制的AI辅助工具,期望通过对表输入需求即可得到处理后的数据(想起来很棒),减少额外的操作,辅助相关工作人员(会计,教师等)更简单的工作.Cha ...
- 文心一言 VS 讯飞星火 VS chatgpt (170)-- 算法导论13.2 3题
三.用go语言,设在图 13-2 左边一棵树中,a.b和c 分别为子树a.β和γ中的任意结点.当结点 x 左旋之后,a.b和c 的深度会如何变化? 文心一言: 在二叉树中,左旋操作是改变节点的子节点顺 ...
- 前端布局flex从入门到入土
前端布局flex从入门到入土 作为一个后端,谈不上多会前端,但是一些常见的布局都可以做到,例如flex布局.推荐菜鸟教程的布局:https://www.runoob.com/w3cnote/flex- ...
- flutter去除隐藏标题栏
使用 SystemChrome 类来控制系统 UI 元素的显示和隐藏 import 'package:flutter/material.dart'; import 'package:flutter/s ...
- Java 查找并替换PDF中的指定文本
本文介绍通过Java程序批量替换PDF中的指定文本内容. 1. 程序环境准备如下: 程序使用环境如图,需要注意的是,本文使用了免费版的PDF jar工具:另外JDK版本建议使用高版本更佳. jar ...
- 云小课|手把手教您在PyCharm中连接云端资源进行代码调试
摘要:让我们看看如何在PyCharm中连接云端资源进行代码调试吧! 本文分享自华为云社区<[云小课]EI第54课 手把手教您在PyCharm中连接云端资源进行代码调试>,作者:Hello ...