浅浅的分析Spring底层事务原理

news2024/11/20 6:24:09

Spring事务底层原理

  • 一、@EnableTransactionManagement工作原理
  • 二、Spring事务基本执行原理
  • 三、Spring事务的过程
  • 四、Spring事务传播机制
  • 五、Spring事务传播机制分类
    • (1)案例分析、情况1
    • (2)案例分析、情况2
    • (3)案例分析、情况3
    • (4)案例分析、情况4
  • 六、Spring事务强制回滚
  • 七、TransactionSynchronization

在这里插入图片描述

一、@EnableTransactionManagement工作原理

开启Spring事务本质上就是增加了一个Advisor,但我们使用 @EnableTransactionManagement注解来开启Spring事务是,该注解代理的功能就是向Spring容器中添加了两个Bean:

1AutoProxyRegistrar2ProxyTransactionManagementConfiguration

(1)AutoProxyRegistrar
主要的作用是向Spring容器中注册了一个InfrastructureAdvisorAutoProxyCreator的Bean。
而InfrastructureAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,所以这个类的主要作用就是开启自动代理的作用,也就是一个BeanPostProcessor,会在初始化后步骤中去寻找Advisor类型的Bean,并判断当前某个Bean是否有匹配的Advisor,是否需要利用动态代理产生一个代理对象。

(2)ProxyTransactionManagementConfiguration是一个配置类,它又定义了另外三个bean:

bean定义
BeanFactoryTransactionAttributeSourceAdvisor一个Advisor。
AnnotationTransactionAttributeSource相当于BeanFactoryTransactionAttributeSourceAdvisor中的Pointcut。就是用来判断某个类上是否存在@Transactional注解,或者判断某个方法上是否存在@Transactional注解的。
TransactionInterceptor相当于BeanFactoryTransactionAttributeSourceAdvisor中的Advice;就是代理逻辑,当某个类中存在@Transactional注解时,到时就产生一个代理对象作为Bean,代理对象在执行某个方法时,最终就会进入到TransactionInterceptor的invoke()方法。。

二、Spring事务基本执行原理

一个Bean在执行Bean的创建生命周期时,会经过InfrastructureAdvisorAutoProxyCreator的初始化后的方法,会判断当前Bean对象是否BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配逻辑为判断该Bean的类上是否存在@Transactional注解,或者类中的某个方法上是否存在@Transactional注解,如果存在则表示该Bean需要进行动态代理产生一个代理对象作为Bean对象。
该代理对象在执行某个方法时,会再次判断当前执行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配则执行该Advisor中的TransactionInterceptor的invoke()方法
执行基本流程为:

  1. 利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接
  2. 修改数据库连接的autocommit为false
  3. 执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql
  4. 如果没有抛异常,则提交
  5. 如果抛了异常,则回滚

三、Spring事务的过程

1、数据库:建立连接、开启事务、进行sql操作、成功提交、失败回滚
2、业务逻辑:准备工作(可以进行前置通知)、开启事务、事务操作、成功提交(可以后置通知)、失败回滚(异常通知)
spring的事务是由aop实现的,首先要生成具体的代理对象,然后按照aop流程执行具体
的操作逻辑,正常情况下要通过通知来完成核心功能,但是事务部署通过通知来实现的,
而是通过TransactionInterceptor来实现的,然后调用invoke来实现具体的逻辑。
步骤如下:
1、先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开始新事务。

2、当需要开启的时候获取数据库连接,关闭自动提交功能,开启事务。

3、执行具体的sql逻辑操作,在操作的过程中如果执行失败会通过 completeTransactionafterthrowing来完成事务的回滚操作,回滚的
具体逻辑是通过dorollback方法实现,实现时也要先获取连接对象,
然后通过连接对象进行回滚(conn.rollback)。

4、如果执行成功,那么通过completeTransactionafterrunning来完成事务的提交操作,
具体逻辑是通过docommit方法来实现,实现的时候也是先获取连接,通过连接对象来
进行提交(conn.commit)。

5、最后事务执行完毕需要清除事务相关的事务信息(cleanupTransactioninfo)。

四、Spring事务传播机制

在开发过程中,经常会出现一个方法调用另外一个方法,那么这里就涉及到了多种场景,比如a()调用b():

  1. a()和b()方法中的所有sql需要在同一个事务中吗?
  2. a()和b()方法中的所有sql需要在同一个事务中吗?
  3. a()需要在事务中执行,b()还需要在事务中执行吗?
  4. 或者其他情况
    这种情况下就要求Spring事务能支持上面各种场景,这就是Spring事务传播机制的由来。
    那Spring事务传播机制是如何实现的呢?

先描述其中一个场景中情况,a()在一个事务中执行,调用b()方法时需要新开一个事务执行:

  1. 代理对象执行a()方法前,先利用事务管理器新建一个数据库连接a
  2. 将数据库连接a的autocommit改为false
  3. 把数据库连接a设置到ThreadLocal中
  4. 执行a()方法中的sql
  5. 执行a()方法过程中,调用了b()方法(注意用代理对象调用b()方法)
  6. a()方法正常执行完,则从ThreadLocal中拿到数据库连接a进行提交

关于步骤5的一些详细解释:
1、代理对象执行b()方法前,判断出来了当前线程中已经存在一个数据库连接a了,表示当前线程其实已经拥有一个Spring事务了,则进行挂起
2、挂起就是把ThreadLocal中的数据库连接a从ThreadLocal中移除,并放入一个挂起资源对象中
3、挂起完成后,再次利用事务管理器新建一个数据库连接b
4、将数据库连接b的autocommit改为false
5、把数据库连接b设置到ThreadLocal中
6、执行b()方法中的sql
7、b()方法正常执行完,则从ThreadLocal中拿到数据库连接b进行提交
8、提交之后会恢复所挂起的数据库连接a,这里的恢复,其实只是把在挂起资源对象中所保存的数据库连接a再次设置到ThreadLocal中

过程中最为重要的是在执行某个方法时,判断当前是否已经存在一个事务,就是判断当前线程的ThreadLocal中是否存在一个数据库连接对象,如果存在则表示已经存在一个事务了。

五、Spring事务传播机制分类

在这里面,以非事务方式运行,表示以非Spring事务运行,表示在执行这个方法时,Spring事务管理器不会去建立数据库连接,执行sql时,由Mybatis或JdbcTemplate自己来建立数据库连接来执行sql。

(1)案例分析、情况1

默认情况下传播机制为REQUIRED,表示当前如果没有事务则新建一个事务,如果有事务则在当前事务中执行。

@Component
public class UserService {
 	@Autowired
 	private UserService userService;

 	@Transactional
 	public void test() {
  		// test方法中的sql
  		userService.a();
 	}

 	@Transactional
 	public void a() {
  		// a方法中的sql
 	}
}

所以情况1的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、执行a方法中的sql
5、执行conn的commit()方法进行提交

(2)案例分析、情况2

如果是这种情况:

@Component
public class UserService {
 	@Autowired
 	private UserService userService;

 	@Transactional
 	public void test() {
  		// test方法中的sql
  		userService.a();
        int result = 100/0;
 	}

 	@Transactional
 	public void a() {
  		// a方法中的sql
 	}
}

所以情况2的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、执行a方法中的sql
5、抛出异常
6、执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉

(3)案例分析、情况3

@Component
public class UserService {
 	@Autowired
 	private UserService userService;

 	@Transactional
 	public void test() {
  		// test方法中的sql
  		userService.a();
 	}

 	@Transactional
 	public void a() {
  		// a方法中的sql
        int result = 100/0;
 	}
}

所以情况3的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、执行a方法中的sql
5、抛出异常
6、执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉

(4)案例分析、情况4

@Component
public class UserService {
 	@Autowired
 	private UserService userService;

 	@Transactional
 	public void test() {
  		// test方法中的sql
 	 	userService.a();
 	}

 	@Transactional(propagation = Propagation.REQUIRES_NEW)
 	public void a() {
  		// a方法中的sql
  		int result = 100/0;
 	}
}

所以情况3的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、又新建一个数据库连接conn2
5、执行a方法中的sql
6、抛出异常
7、执行conn2的rollback()方法进行回滚
8、继续抛异常,对于test()方法而言,它会接收到一个异常,然后抛出
9、执行conn的rollback()方法进行回滚,最终还是两个方法中的sql都回滚了

六、Spring事务强制回滚

正常情况下,a()调用b()方法时,如果b()方法抛了异常,但是a()方法捕获了,那么a()的事务还是会正常提交的,但是有的时候,我们捕获异常可能只是不把异常信息返回给客户端,而是为了返回一些更优良的错误信息,所以在这个时候,我们还是希望事务能回滚的,那就得告诉Spring把当前事务回滚掉,做法就是:

@Transactional
public void test(){
 
    // 执行sql
 	try {
  		b();
 	} catch (Exception e) {
  	// 构造友好的错误信息返回
  	TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
 	}
    
}

public void b() throws Exception {
 	throw new Exception();
}

七、TransactionSynchronization

Spring事务有可能会提交,回滚、挂起、恢复,所以Spring事务提供了一种机制,可以让程序员来监听当前Spring事务所处于的状态。

@Component
public class UserService {

 @Autowired
 private JdbcTemplate jdbcTemplate;

 @Autowired
 private UserService userService;

 @Transactional
 public void test(){
  TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

   @Override
   public void suspend() {
    System.out.println("test被挂起");
   }

   @Override
   public void resume() {
    System.out.println("test被恢复");
   }

   @Override
   public void beforeCommit(boolean readOnly) {
    System.out.println("test准备要提交");
   }

   @Override
   public void beforeCompletion() {
    System.out.println("test准备要提交或回滚");
   }

   @Override
   public void afterCommit() {
    System.out.println("test提交成功");
   }

   @Override
   public void afterCompletion(int status) {
    System.out.println("test提交或回滚成功");
   }
  });

  jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");
  System.out.println("test");
  userService.a();
 }

 @Transactional(propagation = Propagation.REQUIRES_NEW)
 public void a(){
  TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {

   @Override
   public void suspend() {
    System.out.println("a被挂起");
   }

   @Override
   public void resume() {
    System.out.println("a被恢复");
   }

   @Override
   public void beforeCommit(boolean readOnly) {
    System.out.println("a准备提交");
   }

   @Override
   public void beforeCompletion() {
    System.out.println("a准备提交或回滚");
   }

   @Override
   public void afterCommit() {
    System.out.println("a提交成功");
   }

   @Override
   public void afterCompletion(int status) {
    System.out.println("a提交或回滚成功");
   }
  });

  jdbcTemplate.execute("insert into t1 values(2,2,2,2,'2')");
  System.out.println("a");
 }

}

在这里插入图片描述
记得一键三连哦!!!!!

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

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

相关文章

【正点原子FPGA连载】第三十二章OV5640摄像头RGB-LCD显示实验 摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0

1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第三十二章OV564…

深度学习——批量归一化(笔记)

主流的卷积网络基本都设计批量归一化这个层 1.为什么要批量归一化? ①网络层很深,数据在底层,损失函数在最顶层。反向传播后,顶层的梯度大,所以顶层训练的较快。数据在底层,底层的梯度小,底层训…

【开源项目】震惊JDBC查询比MyBatis查询慢

震惊JDBC查询比MyBatis查询快? 文章编写起始原因,在编写项目的时候碰到一个深坑,JDBC获取5000条数据,居然耗时261s,MyBatis同样的操作,耗时12s左右,震惊。看到这里下巴都快掉下来了。不是网上都…

Pyqt5 Key value动态创建 QTreeWidget

在自己的应用上,需要根据读取的 值来创建 目录与子页,并打开对应的界面 实现思路 1、定义数组 存放 {(Key value index ).....(Key_n value_n index_n )} 2、获取相关数据&#x…

【Java开发】 Spring 09 :Spring Data REST 实现并访问简单的超媒体服务

Spring Data REST 是提供一个灵活和可配置的机制来编写可以通过HTTP公开的简单服务,简单来说,而且可以省去大部分controller和services的逻辑,因为Spring Data REST 已经为你都做好了,目前支持JPA、MongoDB、Neo4j、Solr、Cassand…

Ribbon负载均衡

Ribbon负载均衡 Ribbon是微服务架构中,可以作为负载均衡的技术实现,如下图所示 Ribbon负载均衡 1、消费者发起请求2、被负载均衡拦截器拦截3、将请求信息交给RibbonLoadBanlancerClient4、获取url的服务id5、DynamicServerListLoadBalancer拿到id去eur…

java基础巩固-宇宙第一AiYWM:为了维持生计,架构知识+分布式微服务+高并发高可用高性能知识序幕就此拉开(二:网关balabala)~整起

上集,在架构知识分布式微服务高并发高可用高性能知识序幕就此拉开(一:总览篇)中,说到了 当用户请求过来时,这个请求或者说URL先到服务调用端【咱们之前的项目中的Controller其实就算是一个服务调用方&#…

VMware ESXi 8.0 SLIC Unlocker 集成网卡驱动和 NVMe 驱动 (集成驱动版)

发布 ESXi 8.0 集成驱动版,在个人电脑上运行企业级工作负载 请访问原文链接:VMware ESXi 8.0 SLIC & Unlocker 集成网卡驱动和 NVMe 驱动 (集成驱动版),查看最新版。原创作品,转载请保留出处。 作者主页:www.sysi…

【Pytorch】第 3 章 :进行数值估计的蒙特卡洛方法

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎 📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃 🎁欢迎各位→点赞…

Java ssh框架 mysql实现的进销存管理系统源码+运行教程+文档

今天给大家演示一下一款由sshmysql实现的进销存管理系统,其中struts版本是struts2,这个系统的功能非常完善,简直可以说是牛逼,到了可以用于企业直接商用的地步,此外该项目还带有完整的论文,是Java学习者及广…

Spark 初识

文章目录Spark 初识Spark是什么Apache Spark演变为什么使用Spark全快Spark组件Spark CoreSpark SQLSpark StreamingSpark MLlibSpark GraphXSparkRpySparkspark 在数仓的应用总结Spark 初识 从今天开始我们进入数据仓库的查询引擎篇了,前面我们已经写了大量的文章介…

三分钟了解LAP编程框架

针对Java开发者的灵魂拷问: 1、梳理的流程,关键逻辑是否有遗漏,理解一致吗? 2、设计时,如何更方便的与产品沟通?原有的设计是否有不合理的?绘制的流程图大家都能理解吗? 3、测试时&a…

316页11万字AI赋能智慧水利大数据信息化平台建设和运营解决方案

第一章 系统综述 1.1 项目背景 1.2 系统概述 1.3 需求分析 1.3.1 中心管控需求 1.3.2 前端监测需求 1.4 建设目标 1.5 设计原则 1.6 设计依据 第二章 系统总体设计 2.1 总体设计思路 2.2 架构设计 2.2.1 逻辑架构 2.2.2 系统架构 2.3 关键技术应用 2.4 系统特色…

代码随想录刷题|LeetCode 647. 回文子串 516.最长回文子序列

647. 回文子串 题目链接:https://leetcode.cn/problems/palindromic-substrings/ 思路 动态规划思路 1、确定dp数组 布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为tr…

【真的?】用 ChatGPT 写一篇 Python 翻译库博客,可以打 9 分

今天来个大的实践项目,用 ChatGPT 写一篇博客,冲击一下热榜! 从零开始玩 ChatGPT⛳️ ChatGPT 亮点⛳️ 账号篇⛳️ 第一次使用⛳️ 用 Python 实现一个英汉互译的小程序⛳️ googletrans 库核心用法⛳️ 再补充一些知识点⛳️ googletrans 和…

功率放大电路和电压放大电路的区别是什么意思

功率放大电路和电压放大电路都属于模拟电路,是工程师日常经常用到的比较常见的模拟电路,很多小白工程师对于功率放大电路和电压放大电路的区别都很好奇,下面就来看看区别有哪些。 图:功率放大电路与电压放大电路对比 1、功能和基本…

docker之网络配置

目录一、网络模式1.bridge模式(默认模式)2.host模式3.初识网络模式二、bridge模式三、host模式四、自定义网络一、网络模式 Docker在创建容器时有四种网络模式:bridge/host/container/none,bridge为默认不需要用–net去指定,其他三种模式需要…

微服务框架 SpringCloud微服务架构 19 文档操作 19.2 修改文档

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式,系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构19 文档操作19.2 修改文档19.2.1 修改文档19.2.2 总结19 文档操作 19.2 修…

推荐一款超级好用的工具:uTools详解使用

介绍 uTools 是什么?下载并安装uTools 能做什么?一切皆插件超级面板 uTools 是什么? uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。 通过快捷键(默认 alt…

红队隧道应用篇之CS正反向连接突破内网(二)

正向连接 环境拓扑图 操作步骤 在CS客户端新建一个TCP协议的监听, 监听端口为4444 创建无状态木马(Windows Executable(S)), 选择上述建立的TCP监听器, 随后将无状态木马放到不出网的内网主机中去运行, 运行后内网主机就会监听本机的4444端口 在web服务器的beacon命令行输入:…