问题概述

在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事务失效原因的更多相关文章

  1. spring transaction源码分析--事务架构

    1. 引言  事务特性 事务是并发控制的单元,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性.事 ...

  2. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  3. Spring AMQP 源码分析 02 - CachingConnectionFactory

    ### 准备 ## 目标 了解 CachingConnectionFactory 在默认缓存模式下的工作原理   ## 前置知识   <Spring AMQP 源码分析 01 - Impatie ...

  4. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  5. 5.2 spring5源码--spring AOP源码分析二--切面的配置方式

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

  6. Spring Security 源码分析(四):Spring Social实现微信社交登录

    社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...

  7. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

  8. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  9. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  10. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

随机推荐

  1. 【算法】Java版

    二分查找算法 二分查找算法(Binary Search Algorithm)是一种在有序数组中查找特定元素的搜索算法.该算法的基本思想是将数组从中间分成两部分,然后与目标元素进行比较,进而确定目标元素 ...

  2. Java:字符串(String)类型转成整型(int)的方法

    Java:字符串(String)类型转成整型(int)的方法 使用 Integer.parseInt() 或 Integer.valueOf() 将 String 转换为 int. 其中: Integ ...

  3. ElasticSearch之文件描述符的数量

    ElasticSearch在运行过程中,涉及大量文件的打开.关闭.读.写等操作.因此当ElasticSearch进程的文件描述符数量不足时可能导致丢失数据等故障现象. 因此为保障ElasticSear ...

  4. vulnhub - Aragog - writeup

    信息收集 目标开放了80.22端口. root@Lockly temp/tmp » arp-scan -I eth1 -l Interface: eth1, type: EN10MB, MAC: 00 ...

  5. Python——第一章:循环语句while——break和continue

    while True: content = input("请输入你要发送的内容(q结束):") print("发送内容:", content) 这样的代码会无限 ...

  6. 一文读懂Spring框架中依赖注入流程

    想读懂Spring的依赖注入流程,我们先简单了解一下Ioc和DI是什么? IoC和DI Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计 ...

  7. struts2 Filter中无法转发请求

    struts2 Filter中无法转发请求 项目升级struts2版本为最新以修复漏洞,由于一些历史原因,部分访问在升级后访问404,直接对历史代码改造代价太大. 于是使用拦截器对其转发.重定向,但是 ...

  8. 9、线性布局(Row和Column)

    自定义的IconContainer void main() { runApp(MaterialApp( theme: ThemeData(primarySwatch: Colors.yellow), ...

  9. 【API 进阶之路】做 OCR 文字识别,谁说必须要有 AI 工程师?

    摘要:有些功能还真不能光凭自己的直觉和认识,来自一线的声音才是最真实的用户需求.比方说名片录入的需求. 在公司技术委员会副主席这个位置上干了有几个月了,期间,我一方面给研发团队整理各种文档资料,做技术 ...

  10. 看故事学Redis:再不懂,我怀疑你是假个开发

    摘要:还不懂Redis?看完这个故事就明白了! 本文转载自博客园社区<还不懂Redis?看完这个故事就明白了!>,作者:轩辕之风 我是Redis 你好,我是Redis,一个叫Antirez ...