人人都能看懂的Spring源码解析,Spring声明式事务关于传播特性、事务挂起与恢复的处理

news2024/11/15 2:02:32

人人都能看懂的Spring源码解析,Spring声明式事务关于传播特性、事务挂起与恢复的处理

  • 原理解析
    • AbstractPlatformTransactionManager
    • 事务传播特性
    • 事务挂起与恢复
    • 通过DataSourceTransactionManager看事务挂起和恢复的具体实现
  • 代码走读
  • 总结

往期文章:

  1. 人人都能看懂的Spring底层原理,看完绝对不会懵逼
  2. 简单易懂的Spring扩展点详细解析,看不懂你来打我
  3. 人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册
  4. 简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑
  5. 简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化
  6. 人人都能看懂的Spring源码解析,Spring如何解决循环依赖
  7. 人人都能看懂的Spring源码解析,Spring如何处理AOP
  8. 人人都能看懂的Spring源码解析,Spring声明式事务是如何实现的

上一篇文章说到Spring声明式事务的实现原理,了解到了Spring声明式事务通过AOP织入编程式事务的流程代码。但是遗留了一些细节没有说明,比如Spring声明式事务是如何处理传播特性的?如何实现事务的挂起和恢复?本篇文章即对这些细节问题进行讲解。

原理解析

AbstractPlatformTransactionManager

上一篇文章已经对PlatformTransactionManager事务管理器进行讲解,了解到它有三个抽象方法getTransaction、commit、rollback(分别对应开启事务并获取事务对象、提交、回滚)需要我们实现。但其实Spring已经提供了一个抽象事务管理器AbstractPlatformTransactionManager,实现了这三个方法,而另外预留了 doGetTransaction(获取事务对象)、isExistingTransaction(当前事务对象是否存在事务)、doBegin(开启事务)、doSuspend(挂起事务)、doResume(唤醒事务)、doCommit(提交事务)、doRollback(回滚事务) 等方法让继承它的子类去重写。而原先的getTransaction、commit、rollback三个方法将作为模板方法被直接调用

在这里插入图片描述

事务传播特性

Spring声明式事务的传播特性,就在AbstractPlatformTransactionManager的getTransaction方法中进行处理,这个逻辑作为模板方法中的固定的流程代码,每一个子类事务处理器通过继承AbstractPlatformTransactionManager都可以复用。

AbstractPlatformTransactionManager的getTransaction方法首先调用doGetTransaction方法获取事务对象。然后会调用isExistingTransaction判断当前是否存在事务,根据isExistingTransaction的返回值(true / false),处理流程会进入不同的分支,不同的分支涉及不同的传播特性的处理。

如果isExistingTransaction方法返回true,代表当前存在事务。判断传播特性是否是PROPAGATION_NEVER,如果是,则报错;否则判断传播特性是否是PROPAGATION_NOT_SUPPORTED,如果是,则挂起当前事务,以非事务方式运行;否则判断当前事务传播特性是否是PROPAGATION_REQUIRES_NEW,如果是,则挂起当前事务,开启新的事务;否则判断传播特性是否是PROPAGATION_NESTED,如果是,则保存回滚点;否则,就在当前事务中运行。

如果isExistingTransaction方法返回false,代表当前不存在事务。判断当前事务传播特性是否是PROPAGATION_MANDATORY,如果是,则报错;否则判断当前事务的传播特性是否是PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEWPROPAGATION_NESTED三种中的一种,如果是,则开启新的事务;否则,在非事务下运行。

在这里插入图片描述

事务挂起与恢复

可以看到上面的某些传播特性是需要挂起当前事务的,那么有挂起就有恢复

挂起事务的方法就是AbstractPlatformTransactionManager的suspend方法,suspend方法又会调用doSuspend,该方法是需要子类去重写的,否则就会抛出TransactionSuspensionNotSupportedException异常。也就是说挂起事务的处理交给了我们去实现,Spring只是留了个口子。

这个suspend方法会在getTransaction方法中,判断如果要挂起当前事务时,就会被调用。然后getTransaction方法中判断如果要开启新的事务,则调用startTransaction方法,startTransaction方法调用doBegin方法。

在这里插入图片描述

而恢复方法就是resume方法,resume方法又会调用doResume方法。doResume也是需要子类去重写的,否则就会抛出TransactionSuspensionNotSupportedException异常。

resume方法在processRollback方法和processCommit方法的最后被调用。commit方法判断当前事务需要回滚,则调用processRollback方法,否则调用processCommit方法。rollback方法则会调用processRollback方法。

因此Spring已经封装好了事务挂起和恢复的处理流程,而具体如何挂起、如果恢复,则需要子类去实现。

在这里插入图片描述

通过DataSourceTransactionManager看事务挂起和恢复的具体实现

spring-jdbc提供了一个AbstractPlatformTransactionManager的实现类,名字叫做DataSourceTransactionManager

spring-jdbc通过ConnectionHolder管理连接对象Connection,然后通过TransactionSynchronizationManager事务同步管理器管理ConnectionHolder对象。

TransactionSynchronizationManager里面有一个ThreadLocal<Map<Object, Object>>类型的成员变量resources,专门用于管理不同线程的ConnectionHolder对象。里面是一个Map类型,spring-jdbc把DataSource作为key,把ConnectionHolder作为vallue。之所以设计成这样的类型,是应该考虑到一个工程可能有多个DataSource,然后不同的线程在不同的DataSource上,有不同的连接Connection。

在这里插入图片描述

DataSourceTransactionManager的doBegin方法会拿到当前事务管理器的DataSource,调用getConnection方法获取连接,然后封装成ConnectionHolder,调用TransactionSynchronizationManager的bindResource方法,把ConnectionHolder放入到resources里面。

在这里插入图片描述

DataSourceTransactionManager的doSuspend会调用TransactionSynchronizationManager的unbindResource把当前线程当前DataSource对应的ConnectionHolder对象从resources中去掉,然后把该ConnectionHolder对象返回。返回的ConnectionHolder对象,Spring会帮他hold住,在调用doResume方法时会还给它。

在这里插入图片描述

DataSourceTransactionManager的doResume方法接收到Spring交还给它的ConnectionHolder,会重新调用TransactionSynchronizationManager的bindResource方法,把ConnectionHolder放入到resources里面。

在这里插入图片描述

代码走读

在这里插入图片描述

AbstractPlatformTransactionManager#getTransaction方法,首先调用doGetTransaction方法获取事务对象。

在这里插入图片描述

然后进入DataSourceTransactionManager#doGetTransaction方法,new一个DataSourceTransactionObject对象,然后调用TransactionSynchronizationManager的getResource方法获取连ConnectionHolder,obtainDataSource方法返回当前事务管理器的DataSource。

在这里插入图片描述
然后进入到TransactionSynchronizationManager的getResource方法,getResource调用doGetResource方法。

在这里插入图片描述
在这里插入图片描述

doGetResource从resources中获取当前线程当前DataSource对应的ConnectionHolder。

但是如果之前没有开启过事务的话,这里返回的ConnectionHolder就是null。

回到AbstractPlatformTransactionManager#getTransaction方法。

在这里插入图片描述

获取到事务对象之后,就会调用isExistingTransaction方法判断当前是否存在事务。

在这里插入图片描述

进入到DataSourceTransactionManager#isExistingTransaction,就是判断事务对象中是否存在ConnectionHolder,如果存在则取出set到事务对象中的ConnectionHolder,调用它的isTransactionActive判断transactionActive属性是否为true。但是因为现在是刚刚进来,所以这里的事务对象是没有ConnectionHolder的。

在这里插入图片描述

如果isExistingTransaction方法返回true,就会进入if分支,处理存在事务的情况。如果isExistingTransaction方法返回false,则不会进入if分支。这里先跳过这个if分支,继续往下看。

在这里插入图片描述
isExistingTransaction方法返回false,代表之前没有开启事务,会首先判断当前事务的传播特性是否是MANDATORY,如果是则抛出错误;否则判断当前事务的传播特性是否是REQUIRED、REQUIRES_NEW、NESTED三者之一,如果是,会先调用suspend方法挂起当前事务,然后调用startTransaction开启新的事务。

这里可能会有疑问,现在的情况不是之前没有开启事务吗?为什么这里要挂起?因为有可能是之前先开启了事务,然后又挂起了,以非事务运行,所以现在也要挂起。这样,当前事务执行完,就会恢复为之前的无事务状态,然后无事务状态也运行完,就会恢复之前挂起的事务。因此无事务状态也要调用suspend方法挂起,否则就会丢失了之前开启的事务。

在这里插入图片描述
如果传播特性不是MANDATORY,也不是REQUIRED、REQUIRES_NEW、NESTED三者之一,就会以非事务运行,没有开始事务,直接返回一个TransactionStatus,里面的事务对象是个null。

在这里插入图片描述

进入startTransaction方法,可以看到调用了doBegin方法。

在这里插入图片描述

DataSourceTransactionManager的doBegin方法判断当前的事务对象没有ConnectionHolder,会调用当前DataSource的getConnection方法获取连接。然后封装为ConnectionHolder对象放入到事务对象中。

在这里插入图片描述

调用connection的setAutoCommit(false)方法,关闭自动提交。

在这里插入图片描述
设置ConnectionHolder的transactionActive属性为true。然后调用TransactionSynchronizationManager的bindResource方法把当前DataSource与ConnectionHolder的映射关系与当前线程绑定。

在这里插入图片描述
TransactionSynchronizationManager的bindResource方法里面就是调用resources.get()获取到当前线程对应的Map,然后DataSource作为key,ConnectionHolder作为value,put到Map中。

如果接下来调用了其他方法,又要开启一个新的事务,就会进入当刚刚跳过的分支。

在这里插入图片描述

如果当前事务的传播特性是NEVER,那么抛出错误。

在这里插入图片描述

如果当前事务的传播特性是NOT_SUPPORTED,会调用suspend方法挂起之前的事务,以非事务运行。

在这里插入图片描述
如果当前事务的传播特性是REQUIRES_NEW,会调用suspend方法挂起之前的事务,然后调用startTransaction方法开启新的事务。

在这里插入图片描述

如果当前事务的传播特性是NESTED,保存一个回滚点,继续往下进行。

如果这些都不是,那么就继续在之前的事务中运行。

在这里插入图片描述

suspend方法会调用doSuspend方法。

在这里插入图片描述

DataSourceTransactionManager的doSuspend方法,调用TransactionSynchronizationManager的unbindResource方法。

在这里插入图片描述
在这里插入图片描述

TransactionSynchronizationManager的unbindResource方法会调用doUnbindResource,从resources中取出当前线程对应的Map,删除对应DataSource的ConnectionHolder,并返回该ConnectionHolder。

进入AbstractPlatformTransactionManager#commit方法。

在这里插入图片描述

AbstractPlatformTransactionManager#commit方法会判断是否需要回滚,如果需要,会调用processRollback方法,如果不需要,则调用processCommit方法。

在这里插入图片描述
在这里插入图片描述
processCommit方法会调用doCommit方法,然后进入DataSourceTransactionManager#doCommit就会直接调用Connection的commit方法提交事务。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后processCommit方法最后会调用cleanupAfterCompletion方法,cleanupAfterCompletion方法会调用resume方法,resume方法会调用doResume方法。

在这里插入图片描述
可以看到DataSourceTransactionManager的doResume方法就是调用TransactionSynchronizationManager的bindResource方法把交还给它的ConnectionHolder又在此设置到resources中。

在这里插入图片描述
在这里插入图片描述

processRollback方法会调用doRollback方法,DataSourceTransactionManager的doRollback方法会调用Connection的rollback方法回滚事务。

在这里插入图片描述

processRollback方法最后也是和processCommit方法一样,会调用cleanupAfterCompletion方法。

在这里插入图片描述
AbstractPlatformTransactionManager#rollback方法则是直接调用processRollback方法,这里就不用再往下看了。

总结

这一篇关于传播特性和事务挂起恢复的讲解,加上上一篇对Spring声明式事务实现原理的讲解,基本上就是对Spring事务讲解的全部内容,下面对Spring事务做一个总结。

  1. Spring事务可以通过编程式事务实现。通过PlatformTransactionManager的getTransaction方法开启事务并获取事务对象;然后在try块中执行我们的业务逻辑;如果报错,则在catch块中调用PlatformTransactionManager的rollback方法进行事务回滚;如果没有报错,则调用PlatformTransactionManager的commit方法进行事务提交。
  2. 而Spring声明式事务则是通过AOP的方式,织入编程式事务的模板代码,使得用户无需在手动编写编程式事务的模板代码。
  3. Spring提供了一个AbstractPlatformTransactionManager实现了PlatformTransactionManager接口,实现了getTransaction、rollback、commit方法,我们可以继承AbstractPlatformTransactionManager类,无需实现PlatformTransactionManager接口。
  4. AbstractPlatformTransactionManager的getTransaction方法会调用doGetTransaction方法获取事务对象,然后根据isExistingTransaction方法的返回值对不同的传播特性进行处理
  5. AbstractPlatformTransactionManager如果要开启新事务,会调用doBegin方法;如果要挂起新事务,会调用doSuspend方法;如果要回滚事务,会调用doRollback方法;如果要提交事务,会调用doCommit方法;如果要恢复挂起事务,则调用doResume方法。这些方法都需要子类去实现。
  6. spring-jdbc的DataSourceTransactionManager就继承了AbstractPlatformTransactionManager。开启事务时会把Connection对象封装会ConnectionHolder放入ThreadLocal中;挂起事务就是从ThreadLocal中把ConnectionHolder去掉;恢复挂起事务则是把之前从ThreadLocal中去掉的ConnectionHolder设置回去。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/564602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LRU Cache

前言 哈喽&#xff0c;各位小伙伴大家好&#xff0c;本章内容为大家介绍计算机当中为了提高数据相互传输时的效率而引进的一种重要设计结构叫做LRU Cache,下面将为大家详细介绍什么是LRU Cache,以及它是如何是实现的&#xff0c;如何提升效率的。 1.什么是LRU Cache? LRU是L…

卷起来了?2023这三个项目直接让你原地起飞!

理论自学谁不会&#xff0c;理论知识跟实战项目实践相结合才是大问题&#xff1f; 还在发愁没有项目练手&#xff1f;还在发愁简历中的项目生搬硬凑&#xff1f;还在担心自己没实操过项目被面试官直接K.O? 这三个实战项目让你快人一步&#xff0c;总有一个适合你的&#xff…

数慧时空20年磨一剑:推出智能遥感云平台DIEY,自然资源多模态大模型“长城”,为地理信息产业提速

作者 | 伍杏玲 出品 | CSDN 据中国地理信息产业发展报告公布的数据&#xff0c;截至2020年末&#xff0c;行业从业单位13.8万家&#xff0c;从业人数336.6万&#xff0c;到2021年末&#xff0c;从业单位增加到16.4万家&#xff0c;从业人数增加到398万&#xff0c;产业规模越…

软件测试的未来?为什么越来越多的公司选择模糊测试

背景&#xff1a;近年来&#xff0c;随着信息技术的发展&#xff0c;各种新型自动化测试技术如雨后春笋般出现。其中&#xff0c;模糊测试&#xff08;fuzz testing&#xff09;技术开始受到行业关注&#xff0c;它尤其适用于发现未知的、隐蔽性较强的底层缺陷。这里&#xff0…

Eclipse MAT分析内存案例

前言 本文记录一次使用Eclipse MAT排查内存问题的案例&#xff0c;缘由是线上某服务OOM&#xff0c;排查得知jvm old区占满&#xff0c;但是gc了还是无法释放 实战 首先在线上服务器排查发现某应用占用了大量的内存&#xff0c;由一个ConcurrentHashMap对象造成的&#xff0…

【ArcGIS Pro二次开发】(31):ArcGIS Pro中的多线程

ArcGIS Pro与旧的ArcGIS桌面应用程序的显著不同之处在于&#xff0c;它采用多线程架构&#xff0c;可以有效的发挥多核CPU的优势。这使得二次开发工具的性能变得更好&#xff0c;但也对开发工作带来了更多的难点和挑战。 一、多线程需要注意的问题 一般情况下&#xff0c;为了…

尚硅谷MyBatis-Plus笔记001【简介、入门案例、基本CRUD】

视频地址&#xff1a;【尚硅谷】MyBatisPlus教程&#xff08;一套玩转mybatis-plus&#xff09;_哔哩哔哩_bilibili 尚硅谷MyBatis-Plus笔记01【简介、入门案例、基本CRUD】 尚硅谷MyBatis-Plus笔记02【】 尚硅谷MyBatis-Plus笔记03【】 尚硅谷MyBatis-Plus笔记04【】 尚硅谷…

非暴力沟通--日常沟通的技巧与实践

这篇文章是我在公司团队内部做的分享的演讲稿 开场白 大家好&#xff0c;我今天要分享的主题是非暴力沟通–日常沟通的技巧与实践。 介绍非暴力沟通这本书 分享这个主题的原因是我最近看了一本书&#xff0c;叫做《非暴力沟通》&#xff0c;这本书是美国一个叫做马歇尔卢森堡…

MFC CListCtrl 显示图片

MFC CListCtrl 显示图片 MFC CListCtrl 显示图片PreCreateWindow中设置风格没有起作用在OnCreate中设置CListCtrl的风格最合适在OnInitialUpdate中添加数据最合适需要设置CImageList&#xff0c;资源是我自己搞的一个图片资源ps:参考链接 MFC CListCtrl 显示图片 在使用MFC的C…

Codeforces Round 764 (Div. 3)

比赛链接 Codeforces Round 764 A. Plus One on the SubsetB. Make APC. Division by Two and PermutationD. Palindromes ColoringE. Masha-forgetful A. Plus One on the Subset Example input 3 6 3 4 2 4 1 2 3 1000 1002 998 2 12 11output 3 4 1题意&#xff1a; 你可…

怎么学习机械学习相关的技术?

学习机器学习相关技术的过程可以分为以下几个步骤&#xff1a; 掌握基本数学和统计知识&#xff1a; 机器学习建立在数学和统计学的基础上&#xff0c;了解线性代数、概率论、统计学等基本概念和方法对于理解机器学习算法至关重要。 学习编程和数据处理&#xff1a; 掌握一门…

kafka基础介绍

目录 前言&#xff1a; 一:kafka架构 1.kafka基础架构 2、kafka多副本架构 二、kafka基础概念 1、produce 2. Consumer 3、Broker ​ 4、Topic 5、Partition 6、Replicas 7、Offset 8、 AR 9、 ISR 10、OSR 11、HW 12、LEO 13、Lag 三、kafka特性 四、kafka…

总结加载Shellcode的各种方式

1.内联汇编加载 使用内联汇编只能加载32位程序的ShellCode&#xff0c;因为64位程序不支持写内联汇编 #pragma comment(linker, "/section:.data,RWE") //将data段的内存设置成可读可写可执行 #include <Windows.h>//ShellCode部分 unsigned char buf[] &qu…

FreeRTOS-任务通知详解

✅作者简介&#xff1a;嵌入式入坑者&#xff0c;与大家一起加油&#xff0c;希望文章能够帮助各位&#xff01;&#xff01;&#xff01;&#xff01; &#x1f4c3;个人主页&#xff1a;rivencode的个人主页 &#x1f525;系列专栏&#xff1a;玩转FreeRTOS &#x1f4ac;保持…

Spring Security入门

1. Spring Security 简介 Spring Security 是一个高度可定制的身份验证和访问控制框架&#xff0c;它基于 Spring 框架&#xff0c;并可与 Spring 全家桶无缝集成。该框架可以精确控制用户对应用程序的访问&#xff0c;控制用户的角色和权限等。 Spring Security 最早是由 Be…

C Primer Plus第三章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 1.通过试验&#xff08;即编写带有此类…

第三节 HLMSEditor场景编辑器的编译

本节注意介绍下HLMSEditor场景编辑器的源码编译使用 一 安装依赖的资源 使用编译器为VS2019 X64&#xff0c;操作系统为WIN10&#xff0c;Ogre2.1&#xff0c;HLMSEditor 注意&#xff1a;为什么不用Ogre2.3?因为HLMSEditor版本为0.5.5&#xff0c;很久没有更新了&#xff0…

【Linux进阶之路】yum与vim操作

文章目录 前言一.yum——Linux的应用商店介绍基本使用① yum源②安装数据传输软件1.将Linux的文件传输到Windows平台上2.将Windows的文件传到Linux系统上 ③删除数据传输软件⑥查看安装包版本⑤练习安装与卸载小火车安装与卸载牛会说话 二.vim —— 一款优雅的编辑器①基本模式…

安卓基础巩固(一):工程结构、基本概念、常用布局、基本组件、动画

文章目录 安卓项目结构AndroidMainfest.xmlres资源目录简介 基本概念LayoutR类 Application与ActivityContextIntent数据传递可传递的数据类型intent.putExtra&#xff08;&#xff09;和使用Bundle的区别数据传递大小的限制 通过Intent 过滤器接收隐式 Intent&#xff1a; 单位…

国内免费的Chatgpt网站分享 支持Ai对话绘图

Chatgpt正式进入大众视野&#xff0c;已半年有余&#xff0c;作为一款媲美于百度、谷歌搜索的工具&#xff0c;它已经成为我们工作、生活、学习中不可缺少的左膀右臂&#xff0c;相比于搜索引擎&#xff0c;它寻找答案&#xff0c;不再需要自己在众多模糊不定的结果中寻找自己需…