【Spring源码核心篇-07】spring事物传播机制的流程和原理

Spring源码核心篇整体栏目


内容链接地址
【一】Spring的bean的生命周期https://zhenghuisheng.blog.csdn.net/article/details/143441012
【二】深入理解spring的依赖注入和属性填充https://zhenghuisheng.blog.csdn.net/article/details/143854482
【三】精通spring的aop的底层原理和源码实现https://zhenghuisheng.blog.csdn.net/article/details/144012934
【四】spring中refresh刷新机制的流程和实现https://zhenghuisheng.blog.csdn.net/article/details/144118337
【五】spring中循环依赖的解决和底层实现https://zhenghuisheng.blog.csdn.net/article/details/144132213
【六】spring中事务的底层实现与执行流程https://zhenghuisheng.blog.csdn.net/article/details/144178500
【七】spring中事物传播机制的流程和原理https://zhenghuisheng.blog.csdn.net/article/details/144178500

spring事物传播机制的流程和原理

    • 一,深入理解spring事物传播机制
    • 1,事务嵌套挂起和恢复原理
    • 2,事务的传播机制
      • 2.1,NEVER传播机制
      • 2.2,NOT_SUPPORTED传播机制
      • 2.3,REQUIRES_NEW传播机制
      • 2.4,NESTED传播机制
    • 3,resume恢复
    • 4,总结

如需转载,请附上链接:https://blog.csdn.net/zhenghuishengq/article/details/144196237

一,深入理解spring事物传播机制

在上一篇讲解了spring事务是如何被扫描和注册到spring容器,然后spring事务底层开启事务、提交事务和回滚事务的底层中心流程。因此要了解spring事务的传播机制和源码实现,需要先熟悉一下上一篇文章,因为很多源码这块都是上一章文章的一些分支源码。

接下来这篇讲解一下spring的传播属性,也是平常时开发中,遇到的比较晦涩难懂的,比如遇到以下的情况,在加了事务的方法insert中插入数据之后,然后又调了加了事务的build方法也要进行插入数据,但是build方法中抛了异常,那么build方法回滚之后insert方法是否需要回滚呢,这就需要深入的理解一下在spring中的传播属性到底是如何实现的

@Transactional
public void insert(){//插入sqlinsert("xxx");//调用build方法build();
}@Transactional
public void build(){//插入sqlinsert("xxx");throw new Exception("xxx");
}

1,事务嵌套挂起和恢复原理

接下来需要先回到getTransaction的方法中,这个方法是开启事务流程中的一个方法,详细的可以看上一篇文章。接下来查看这个方法,内部有一个核心的suspend 事务挂起方法,但是在这个里面的参数设置的值为null,因为这个insert方法上面是开启的第一个事务,前面没有需要事务先创建,因此就没有事务需要先挂起。
在这里插入图片描述

接下来查看这个suspend事务被挂起的方法,首先会判断事务是否被激活,然后调用真正的挂起方法 doSuspend 方法,然后设置一些基本属性,如隔离级别、事务名称等,最后封装成一个 SuspendedResourcesHolder 对象返回

在这里插入图片描述

真正执行挂起的方法如下,会进行一个解绑操作,就是将原来加载到ThreadLocal的事务对应的key-value移除,如在insert中调用的build方法也加了事务,那么在执行build对应的事务时,那么就会将insert的事务先挂起

@Override
protected Object doSuspend(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;txObject.setConnectionHolder(null);return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}

在commit方法和rollback中,会有一个resume方法,然后会有一个真正执行恢复的方法doResume进行事务恢复。在执行完build方法对应的事务之后,又会将insert方法挂起的事务给恢复,然后继续执行insert事务

@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;TransactionSynchronizationManager.bindResource(obtainConnectionFactory(), conHolder);
}

那么通过挂起和恢复两个方法实现事务间的传播机制,那么关于上面的insert事务和build事务的执行流程如下:

  • insert事务是第一个创建的事务,所以不需要挂起和恢复前面的线程,然后创建事务数据源和Connection连接,加入到ThreadLocal变量副本中,随后开启insert对应的事务
  • 第二步就是执行到build方法时,发现上面也有事务注解,然后判断发现在当前事务中已有一个创建的事务,那么就会将前面的insert事务挂起,并且移除ThreadLocal中的key/value,然后继续执行build的数据源和Connection连接,然后存入到ThreadLocal,然后继续开启事务,提交事务等操作
  • 在执行完build的事务之后,然后会恢复上面的insert事务,在ThreadLocal中加入原先的key/value
  • 最后执行insert事务的提交操作或者回滚操作

在这里插入图片描述

2,事务的传播机制

在执行build方法时,在开启事务调用getTransaction方法时,会判断是不是已经存在事务对象

在这里插入图片描述

如果已经有了事务对象,那么就会执行 handleExistingTransaction 方法,后文分析的这些传播进制都是这个方法里面的,如never、not_supported、requires_new、nested等传播机制

private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {...
}

2.1,NEVER传播机制

NEVER类型的设置参数如下

@Transactional(propagation = Propagation.NEVER)

如果传播的事务类型是 PROPAGATION_NEVER 这种never数据类型的话,那么就会直接抛异常报错,因为内部不允许已经存在一个事务,因此如果propagation设置的为NEVER,那么就直接抛异常

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");
}

2.2,NOT_SUPPORTED传播机制

NOT_SUPPORTED类型的设置参数如下

@Transactional(propagation = Propagation.NOT_SUPPORTED)

如果传播机制是这种not_support不支持类型,和默认的传播机制不一样,这种事务也会挂起上一个事务,然后从ThreadLocal中移除这个datasource和Connection的key-value数据源,但是不会开启一个新的事物

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {if (debugEnabled) {logger.debug("Suspending current transaction");}//挂起存在的事物Object suspendedResources = suspend(transaction);boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);//创建一个新的非事物状态(保存了上一个存在事物状态的属性)return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}

在方法内部又会调用一个 prepareTransactionStatus 方法,在这个方法内部就会手动的去创建一个新事物,包括创建数据源和Connection连接这些,因此这样就保证了事物的NOT_SUPPORTED机制,最后也会产生两个事物,一个是insert 事务,一个是 build 事务。

在这里插入图片描述

这种底层是通过jdbcTemplate这种方式建立连接并执行事务的,顾名思义不支持事务,意思就不会用spring原生的事务去操作jdbc,而是直接手动的通过jdbcTemplate这种方式操作jdbc。

2.3,REQUIRES_NEW传播机制

REQUIRES_NEW传播机制类型参数设置如下

@Transactional(propagation = Propagation.PROPAGATION_REQUIRES_NEW)

如果传播机制是这种requires_new创建一个新事物的类型,这个就是前面讲解过的逻辑,会先将前边创建的事务先挂起,然后开启一个新事物,创建新的datasource数据源,Connection连接,最后加入到ThreadLocal变量副本中,就是上面3.1的事务的嵌套和挂起,spring默认就是使用这种传播机制

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {//挂起已经存在的事物SuspendedResourcesHolder suspendedResources = suspend(transaction);try {// 是否需要新开启同步boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);//创建一个新的事物状态(包含了挂起的事务的属性)DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);//开启新的事物doBegin(transaction, definition);//把新的事物状态设置到当前的线程变量中去prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error beginEx) {resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}
}

2.4,NESTED传播机制

NESTED传播机制类型参数设置如下

@Transactional(propagation = Propagation.NESTED)

使用这种方式就是和原生的使用方式一直,就是类似于直接将build写在insert方法里面了。但是build方法本身不支持提交或者回滚,而是依赖于insert的事务。如果build本身回滚,那也不会影响insert的执行;但是如果insert方法出现了回滚,那么肯定是会回滚build方法的

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {// 是否支持保存点:非JTA事务走这个分支。AbstractPlatformTransactionManager默认是true,// JtaTransactionManager复写了该方法false,DataSourceTransactionManager没有复写,还是true,if (useSavepointForNestedTransaction()) {//开启一个新的事物DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);// 为事物设置一个回退点// savepoint 可以在一组事务中,设置一个回滚点,点以上的不受影响,点以下的回滚。(外层影响内层, 内层不会影响外层)status.createAndHoldSavepoint();return status;}else { //  JTA事务走这个分支,创建新事务boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);doBegin(transaction, definition);prepareSynchronization(status, definition);re turn status;}
}

3,resume恢复

在一中大概讲了一些一下suspend挂起的源码和实现,接下来看一些resume事务恢复的原理。接下来直接查看提交或者回滚中的这个 cleanupAfterCompletion 方法,内部主要就是这个 resume 方法

private void cleanupAfterCompletion(DefaultTransactionStatus status) {status.setCompleted();if (status.isNewSynchronization()) {TransactionSynchronizationManager.clear();}if (status.isNewTransaction()) {doCleanupAfterCompletion(status.getTransaction());}if (status.getSuspendedResources() != null) {Object transaction = (status.hasTransaction() ? status.getTransaction() : null);resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());}
}

接下来查看resume方法,内部会执行doResume方法,用于真正的去恢复被挂起的事务。除此之外,还会设置事务的一些属性,原先在事务被挂起的时候这些事务是否激活,事务的隔离级别,名字等都设置在SuspendedResourcesHolder 对象中,因此这里直接从这里面获取相应的属性即可。

在这里插入图片描述

接下来查看 DataSourceTransactionManager 类中的doResume方法,

@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}

然后查看这段绑定资源的代码,安全的将数据源和Connection绑定

在这里插入图片描述

最后在resume方法中,还有一个 doResumeSynchronization 方法,会将这个被挂起的事务全部的恢复

private void doResumeSynchronization(List<TransactionSynchronization> suspendedSynchronizations) {TransactionSynchronizationManager.initSynchronization();for (TransactionSynchronization synchronization : suspendedSynchronizations) {synchronization.resume();TransactionSynchronizationManager.registerSynchronization(synchronization);}
}

4,总结

spring的传播机制主要是跟事务的嵌套有关,当发生事务嵌套时,spring内部会通过挂起进而恢复的方式执行被嵌套的事务,通过设置不同的传播机制的参数,来决定走哪种传播属性。一般比较常用的传播机制就是NEVER、NOT_SUPPORTED、REQUIRES_NEW、NESTED这几种机制

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis实现限量优惠券的秒杀

核心&#xff1a;避免超卖问题&#xff0c;保证一人一单 业务逻辑 代码步骤分析 全部代码 Service public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {Resourceprivate ISeckillVoucher…

.NET8/.NETCore 依赖注入:自动注入项目中所有接口和自定义类

.NET8/.NETCore 依赖接口注入&#xff1a;自动注入项目中所有接口和自定义类 目录 自定义依赖接口扩展类&#xff1a;HostExtensions AddInjectionServices方法GlobalAssemblies 全局静态类测试 自定义依赖接口 需要依赖注入的类必须实现以下接口。 C# /// <summary>…

搭建一个基于Web的文档管理系统,用于存储、共享和协作编辑文档

搭建一个基于Web的文档管理系统&#xff0c;用于存储、共享和协作编辑文档 本项目采用以下架构&#xff1a; NFS服务器: 负责存储文档资料。Web服务器: 负责提供文档访问和编辑功能。SELinux: 负责权限控制&#xff0c;确保文档安全。Git服务器: 负责存储文档版本历史&#x…

gitee:创建仓库,存入本地文件至仓库

一、git下载 git:下载与安装-CSDN博客https://blog.csdn.net/weixin_46001736/article/details/144107485?sharetypeblogdetail&sharerId144107485&sharereferPC&sharesourceweixin_46001736&spm1011.2480.3001.8118 二、创建仓库 1、主页面->右上角新增…

计算机网络 —— HTTP 协议(详解)

前一篇文章&#xff1a;网页版五子棋—— WebSocket 协议_网页可以实现websocket吗-CSDN博客 目录 前言 一、HTTP 协议简介 二、HTTP 协议格式 1.抓包工具的使用 2.抓包工具的原理 3.抓包结果 4.HTTP协议格式总结 三、HTTP 请求 1. URL &#xff08;1&#xff09;UR…

关于单片机的原理与应用!

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///目前正在学习C&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于单片…

爬虫专栏第一篇:深入探索爬虫世界:基础原理、类型特点与规范要点全解析

本专栏会对爬虫进行从0开始的讲解&#xff0c;每一步都十分的细致&#xff0c;如果你感兴趣希望多多点赞收藏关注支持 简介&#xff1a;文章对爬虫展开多方面剖析。起始于爬虫的基本概念&#xff0c;即依特定规则在网络抓取信息的程序或脚本&#xff0c;在搜索引擎信息提取上作…

rabbitmq原理及命令

目录 一、RabbitMQ原理1、交换机&#xff08;Exchange&#xff09;fanoutdirecttopicheaders&#xff08;很少用到&#xff09; 2、队列Queue3、Virtual Hosts4、基础对象 二、RabbitMQ的一些基本操作:1、用户管理2、用户角色3、vhost4、开启web管理接口5、批量删除队列 一、Ra…

@antv/x6 再vue中 ,自定义图形,画流程图、数据建模、er图等图形

X6 是基于 HTML 和 SVG 的图编辑引擎&#xff0c;提供低成本的定制能力和开箱即用的内置扩展&#xff0c;方便我们快速搭建 DAG 图、ER 图、流程图、血缘图等应用。 最终效果图 1.安装 npm install antv/x6 --save //x6主要包 npm install antv/x6-vue-shape //使用vue组…

vscode + conda + qt联合开发

安装vscode 安装conda 清华大学开源软件镜像(Anaconda下载)_清华大学镜像-CSDN博客 conda create新建一个环境&#xff0c;激活这个环境&#xff0c;然后安装pyside6 pip install pyside6 -i https://pypi.tuna.tsinghua.edu.cn/simple 安装成功后输入 pip list查看是否安装…

debian 11 虚拟机环境搭建过坑记录

目录 安装过程系统配置修改 sudoers 文件网络配置换源安装桌面mount nfs 挂载安装复制功能tab 无法补全其他安装 软件配置eclipse 配置git 配置老虚拟机硬盘挂载 参考 原来去 debian 官网下载了一个最新的 debian 12&#xff0c;安装后出现包依赖问题&#xff0c;搞了半天&…

WPF DataGrid 列隐藏

Window节点加上下面的 <Window.Resources><FrameworkElement x:Key"ProxyElement" DataContext"{Binding}" /></Window.Resources>然后随便加一个隐藏控件 <ContentControl Content"{StaticResource ProxyElement}" Visi…

【实体配置】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…

手机卡限速丨中国移动5G变3G,网速500kb

以下猜测错误&#xff0c;又有新的猜测&#xff1a;河南移动的卡出省限速。可能是因为流量结算。 “2024年7月1日起&#xff0c;中国移动集团内部将开启跨省流量结算” 在深圳四五年了&#xff0c;之前没有过&#xff0c;就从上个月开始。11月底解除限速&#xff0c;12月刚开…

不同云计算网络安全等级

导读云计算的本质是服务&#xff0c;如果不能将计算资源规模化/大范围的进行共享&#xff0c;如果不能真正以服务的形式提供&#xff0c;就根本算不上云计算。 等级保护定级流程 定级是开展网络安全等级保护工作的 “基本出发点”&#xff0c;虚拟化技术使得传统的网络边界变…

【python】OpenCV—Tracking(10.5)—dlib

文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、涉及到的库函数dlib.correlation_tracker() 6、参考 1、功能描述 基于 dlib 库&#xff0c;实现指定类别的目标检测和单目标跟踪 2、代码实现 caffe 模型 https://github.com/MediosZ/MobileNet-SSD/tree/master/…

通义灵码走进北京大学创新课堂丨阿里云云原生 10 月产品月报

云原生月度动态 云原生是企业数字创新的最短路径。 《阿里云云原生每月动态》&#xff0c;从趋势热点、产品新功能、服务客户、开源与开发者动态等方面&#xff0c;为企业提供数字化的路径与指南。 趋势热点 &#x1f947; 通义灵码走进北京大学创新课堂&#xff0c;与 400…

Flink双流Join

在离线 Hive 中&#xff0c;我们经常会使用 Join 进行多表关联。那么在实时中我们应该如何实现两条流的 Join 呢&#xff1f;Flink DataStream API 为我们提供了3个算子来实现双流 join&#xff0c;分别是&#xff1a; join coGroup intervalJoin 下面我们分别详细看一下这…

Vue3学习宝典

1.ref函数调用的方式生成响应式数据&#xff0c;可以传复杂和简单数据类型 <script setup> // reactive接收一个对象类型的数据 import { reactive } from vue;// ref用函数调用的方式生成响应式数据&#xff0c;可以传复杂和简单数据类型 import { ref } from vue // 简…

C++(4个类型转换)

1. C语言中的类型转换 1. 隐式 类型转换&#xff1a; 具有相近的类型才能进行互相转换&#xff0c;如&#xff1a;int,char,double都表示数值。 2. 强制类型转换&#xff1a;能隐式类型转换就能强制类型转换&#xff0c;隐式类型之间的转换类型强相关&#xff0c;强制类型转换…