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中有一段类似的代码:

spring-transaction源码分析(3)Transactional事务失效原因

spring-transaction源码分析(3)Transactional事务失效原因

其中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方法调用另一个需要被拦截的方法,将不会执行拦截逻辑。

原文链接:https://www.cnblogs.com/xugf/p/17390167.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:spring-transaction源码分析(3)Transactional事务失效原因 - Python技术站

(0)
上一篇 2023年5月11日
下一篇 2023年5月11日

相关文章

  • 如何使用Java字节码操纵库?

    Java字节码操纵库是一个用于读写、操纵Java字节码的工具库,常用于动态生成和修改字节码,实现AOP、代码增强等功能。本文将详细讲解Java字节码操纵库的使用攻略,包括环境配置、库的选择、常用API使用示例等。 环境配置 在开始使用Java字节码操纵库之前,我们需要确保系统已安装JDK,建议使用JDK 8及以上版本。然后,我们需要下载并导入所选的字节码操纵…

    Java 2023年5月11日
    00
  • 一篇文章带你了解MySQL数据库基础

    一篇文章带你了解MySQL数据库基础 概述 MySQL是一个非常流行的关系型数据库管理系统。它被广泛应用于Web应用程序的开发中,因为它能够处理大量的数据,而且速度快且稳定。 这篇文章会介绍MySQL数据库的基础知识,包括如何创建数据库和表格,如何插入和查询数据以及如何使用一些常见的命令和函数。 安装MySQL 在开始使用MySQL数据库之前,我们需要先安装…

    Java 2023年6月16日
    00
  • 详解SpringBoot读取配置文件的N种方法

    下面是详解SpringBoot读取配置文件的N种方法的完整攻略: 1. 前言 SpringBoot是一个灵活、高效的Java框架,可以用来轻松构建Web应用程序。在SpringBoot中,读取配置文件是非常重要的一部分。本文将介绍SpringBoot读取配置文件的N种方法,并附带代码示例。 2. 通过@Value注解读取配置文件 @Value注解是Sprin…

    Java 2023年5月19日
    00
  • 基于Spring Boot保护Web应用程序

    下面我将详细讲解基于Spring Boot保护Web应用程序的完整攻略。 1. 目标 保护Web应用程序是非常重要的,因为Web应用程序经常面临各种来自外部的威胁,如SQL注入、跨站脚本、会话劫持等。本攻略的目标是使用Spring Boot防御这些攻击并保护Web应用程序的安全。具体来说,我们将使用以下方法来实现这个目标: 基于Spring MVC和Thym…

    Java 2023年6月3日
    00
  • springmvc项目使用@Valid+BindingResult遇到的问题

    针对“springmvc项目使用@Valid+BindingResult遇到的问题”,我提供以下完整攻略: 1. 理解问题 经过实践和研究,我们发现当使用@Valid和BindingResult配合进行表单数据校验时,有时会遇到一些问题。 问题的根本原因在于BindingResult的处理方式与我们期望的不太一样,它不会使@Valid注解的校验失败,而是将校…

    Java 2023年5月20日
    00
  • RateLimit-使用guava来做接口限流代码示例

    为保护服务器的正常运行和客户端的正常使用,我们需要对接口进行访问限制。本文介绍了使用Guava RateLimit实现接口限流的代码示例。 Guava RateLimit简介 Guava是由Google开发的Java类库,其中包含了RateLimiter类,可用于接口限流。RateLimiter可以用于限制一定时间内的请求频率,比如每秒钟只能处理10个请求。…

    Java 2023年5月19日
    00
  • feign调用中文参数被encode编译的问题

    当我们使用Feign进行调用时,如果参数中含有中文或其他非ASCII字符,我们会发现这些参数被自动编码了,而且编码方式并不是我们常见的UTF-8,这就需要我们进行一些额外的配置来解决这个问题。 一般情况下,我们需要在Feign配置中添加一个编码器类,用于将参数编码成UTF-8格式,例如: @Configuration public class FeignCo…

    Java 2023年5月20日
    00
  • Spring切面优先级与基于xml的AOP实现方法详解

    Spring切面优先级与基于XML的AOP实现方法详解 在Spring中,切面是一种用于横切关注点的模块化方式。切面可以定义在XML文件中,也可以使用注解方式定义。本文将详细讲解Spring切面优先级和基于XML的AOP实现方法。 1. Spring切面优先级 在Spring中,切面的优先级是由切面的顺序决定的。切面的顺序可以通过实现Ordered接口或使用…

    Java 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部