Spring事务的源码底层实现

news2024/12/24 10:25:27

文章目录

  • 事务
    • 理论执行过程
    • @EnableTransactionManagement
    • 底层实现



事务

在线流程图

在这里插入图片描述



理论执行过程

通过事务管理器创建一个连接对象connection1

设置事务隔离级别、是否只读等

conn1.autocommit(false)

将conn1存入ThreadLocal中Map<DataSource,Connection>

执行目标方法、多条sql:target.method()

			挂起 ---> con1

			通过事务管理器创建一个连接对象connection2

			设置事务隔离级别、是否只读等

			conn2.autocommit(false)

			将conn2存入ThreadLocal中Map<DataSource,Connection>

			执行目标方法、多条sql:target.method()

			根据执行结果进行事务提交/回滚     commit()-->回滚标记true-->rollback()    rollback()-->异常匹配-->commmit()

			恢复 ---> con1 ---> ThreadLocal Map

根据执行结果进行事务提交/ 回滚   commit() ---> 回滚标记true  ---> rollback()   rollback()-->异常匹配-->commmit()



@EnableTransactionManagement

@EnableTransactionManagement内,其实就是导入了一个TransactionManagementConfigurationSelector

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    ...
}

而在TransactionManagementConfigurationSelector类它其实是一个ImportSelector类型的,在该类的selectImports()方法中主要就是导入了两个类

protected String[] selectImports(AdviceMode adviceMode) {
    switch (adviceMode) {
        case PROXY:
            // 默认是PROXY,这里会往Spring容器中导入两个类
            return new String[] {AutoProxyRegistrar.class.getName(),
                                 ProxyTransactionManagementConfiguration.class.getName()};
        case ASPECTJ:
            // 表示不用动态代理技术,用ASPECTJ技术,比较麻烦了
            return new String[] {determineTransactionAspectClass()};
        default:
            return null;
    }
}



  • AutoProxyRegistrar

    简单理解为就是一个BeanPostProcessor,进行AOP的入口

    此类会往Spring容器中添加一个InfrastructureAdvisorAutoProxyCreator的Bean,此类就和进行AOP操作时导入的AnnotationAwareAspectJAutoProxyCreator类一样,他们两个类都是AbstractAutoProxyCreator的子类,也就是说都是一个BeanPostProcessor

    InfrastructureAdvisorAutoProxyCreator它不会去解析@Aspectj切面类,但是它的父类会获取Spring容器中所有的Advisor

    在这里插入图片描述


  • ProxyTransactionManagementConfiguration

    简单理解为

    这个配置类使用@Bean注解往Spring容器中添加了三个bean

    • BeanFactoryTransactionAttributeSourceAdvisor

      它就是一个Advisor,我们知道它是由advice+Pointcut组成。在创建该bean时,会进行依赖注入,在方法形参中注入下面两个类型的bean

    • TransactionAttributeSource

      它和pointcut有关系,Pointcut会用到该类,我们可以把它简单理解为就是一个工具类,它会去解析方法上写的@Transactional注解信息

    • TransactionInterceptor

      就是advice执行链,也就是Interceptor



总结:

  1. 我们要事务要使用AOP这个功能,那么就一定是需要一个BeanPostProcessor来作为启动入口,也就是AutoProxyRegistrar类引入的InfrastructureAdvisorAutoProxyCreator

  2. 在创建普通bean时,在实例化后会取出所有的Advisor和当前bean进行匹配。

    要经过advisor中的Pointcut去进行类和方法匹配,只要是匹配上了就表示当前bean是需要创建一个代理对象的。

    这里也就用上了BeanFactoryTransactionAttributeSourceAdvisorTransactionAttributeSource两个类

  3. 代理对象到时候执行目标方式之前,会经过Pointcut匹配,再到Interceptor链,会进行事务相关的处理,再去调用目标方法。

    这里也就用上了TransactionInterceptor

至此,@EnableTransactionManagement注解的总体功能就是这样,剩下的只是各个类中的详细实现



底层实现

前置知识:

  1. 在创建各个bean时,会在实例化后执行BeanPostProcessor中的后置方法
  2. 在这个过程中会进行AOP操作,把Spring容器中所有的Advisor类型的bean找出来,并和当前正在创建的bean进行匹配
  3. 将匹配筛选后的Advisor集合、当前创建的bean 使用ProxyFactory创建一个代理对象。
  4. 当代理对象调用目标方法时,就会去遍历ProxyFactory中添加的所有Advisor,执行匹配成功的Interceptor链中的各个invoke()方法 --> proceed()方法

当我们使用Spring事务这个功能,在方法上加了@Transactional注解,并通过代理对象调用该方法时,就会最开始进入到Interceptor的invoke()方法中。也就是TransactionInterceptor.invoke(MethodInvocation invocation)方法



最核心的代码就是下面这一段

// 下面几个关键方法就不粘贴源码了,各位小伙伴自行根据最上方流程图去跟源码
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
    // 如果有必要就创建事务,这里就涉及到事务传播机制的实现了
    // TransactionInfo表示一个逻辑事务,比如两个逻辑事务属于同一个物理事务
    TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

    Object retVal;
    try {
        // 执行下一个Interceptor或被代理对象中的方法
        retVal = invocation.proceedWithInvocation();
    }
    catch (Throwable ex) {
        // 抛异常了,则回滚事务,或者
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
    }
    finally {
        cleanupTransactionInfo(txInfo);
    }

    ...

    // 提交事务
    commitTransactionAfterReturning(txInfo);
    return retVal;
}



源码中的小记录:

// Spring源码中数据库连接对象的状态转变
Connection --> ConnectionHolder --> DataSourceTransactionObject(常见)---> SuspendedResourcesHolder(挂起) --> TransactionStatus ---> TransactionInfo



Spring事务的其他操作:

@Component
public class UserService {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Autowired
	private UserService userService;


	@Transactional
	public void test(){

		System.out.println("test()...");
		jdbcTemplate.execute("insert into m_test VALUES(2,100)");

		userService.a();

        // 自己添加多个同步器
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
			@Override
			public void suspend() {
                // 当前事务挂起时执行
				TransactionSynchronization.super.suspend();
			}

			@Override
			public void resume() {
                // 当前事务恢复时执行
				TransactionSynchronization.super.resume();
			}

			@Override
			public void beforeCommit(boolean readOnly) {
                // 当前事务提交前执行
				TransactionSynchronization.super.beforeCommit(readOnly);
			}

			@Override
			public void beforeCompletion() {
                // 当前事务完成前执行,提交或回滚都算是事务完成
				TransactionSynchronization.super.beforeCompletion();
			}

			@Override
			public void afterCommit() {
                // 当前事务提交后执行
				TransactionSynchronization.super.afterCommit();
			}

			@Override
			public void afterCompletion(int status) {
                // 当前事务完成后执行
				TransactionSynchronization.super.afterCompletion(status);
			}
		});

        // 捕捉一个异常,使用一个自定义的数据友好返回前端,但是该方法前面执行的sql还是需要回滚
		try {
			throw new NullPointerException();
		} catch (NullPointerException e) {
            // 事务提交时会判断RollbackOnly,再不提交转而进行回滚
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			return new Result();
		}
	}

	@Transactional()
	public void a(){
		System.out.println("a()...");
		jdbcTemplate.execute("insert into m_test VALUES(3,300)");
	}

}

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

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

相关文章

c++习题01-ljc的暑期兼职

目录 一&#xff0c;题目描述 二&#xff0c;思路 三&#xff0c;伪代码 四&#xff0c;流程图 五&#xff0c;代码 一&#xff0c;题目描述 二&#xff0c;思路 1&#xff0c;根据题目要求需要声明4个变量&#xff1a;a,b,c,d ;牛奶价格a&#xff0c;活动要求b&…

浅析Resource Quota中limits计算机制

前言 在生产环境中&#xff0c;通常需要通过配置资源配额&#xff08;Resource Quota&#xff09;来限制一个命名空间&#xff08;namespace&#xff09;能使用的资源量。在资源紧张的情况下&#xff0c;常常需要调整工作负载&#xff08;workload&#xff09;的请求值&#xf…

java基于ssm+jsp 毕业生就业信息管理系统

1管理员功能模块 管理员输入个人的用户名、密码、角色登录系统&#xff0c;这时候系统的数据库就会在进行查找相关的信息&#xff0c;如果我们输入的用户名、密码不正确&#xff0c;数据库就会提示出错误的信息提示&#xff0c;同时会提示管理员重新输入自己的用户名、密码&am…

MYSQL存储过程的创建

关于存储过程的题目 1、创建存储过程,查看user表中的所有数据 2、创建存储过程avg_order_quantity,返回所有订单的平均工资 3、创建存储过程show_max_bprice,用来查看bookS的单价最贵的价格 4、创建存储过程show_min_bprice,用来查看bookS的单价最低的价格&#xff0c;并将…

JS在线加密简述

JS在线加密&#xff0c;是指&#xff1a;在线进行JS代码混淆加密。通过混淆、压缩、加密等手段&#xff0c;使得JS源代码难以阅读和理解。从而可以有效防止代码被盗用或抄袭&#xff0c;保护开发者的知识产权和劳动成果。常用的JS在线加密网站有&#xff1a;JShaman、JS-Obfusc…

美业管理系统的优势和功能分析,美业系统你选对了吗?Java源码/演示视频分享

在当今竞争激烈的美业市场中&#xff0c;有效的管理对于提高效率、增强客户体验和推动业务增长至关重要。美业管理系统通过其各种功能和优势&#xff0c;成为现代美业企业不可或缺的利器。 本文将探讨美业管理系统的优势和功能&#xff0c;以及它们对美业企业的重要性。 1.预…

来聊聊Redis客户端的概念

写在文章开头 对于每一个建立的连接redis都会通过redisClient来管理建立的socket连接的信息&#xff0c;本文将从源码的分析的角度来剖析的Redis客户端的基本设计和实现。 Hi&#xff0c;我是 sharkChili &#xff0c;是个不断在硬核技术上作死的 java coder &#xff0c;是 C…

通信协议总结

IIC 基本特点 同步&#xff0c;半双工 标准100KHz&#xff0c;最高400KHz&#xff08;IIC主要应用于低速设备&#xff09; 硬件组成 需外接上拉电阻 通信过程 空闲状态 SDA和SCL都处于高电平 开始信号S和终止信号P 在数据传输过程中&#xff0c;当SCL0时&#xff0c;SDA才…

mac14.1.2 M1芯片终端使用brew命令提示“zsh- command not found- brew ”解决方案

mac14.1.2 M1芯片终端使用brew命令提示“zsh- command not found- brew ” 原因&#xff1a;brew默认安装目录在/opt/homebrew/bin&#xff0c;zshrc文件中找不到对应的PATH路径导致。&#xff08;可通过右键finder的图标选择「前往文件”-输入/opt/homebrew/bin」来查看brew是…

【图书推荐】CPython设计与实现“适合所有Python工程师阅读的书籍”

目录 一、图书推荐 |【CPython设计与实现】 1.1、书籍介绍 1.2、内容简介 1.3、适合哪些人阅读 1.4、作者译者简介 1.5、购买链接 一、图书推荐 |【CPython设计与实现】 "深入Python核心&#xff0c;揭秘CPython的设计智慧&#xff01;&#x1f4d6; 对于每一位热衷…

超详细的Pycharm使用虚拟环境搭建Django项目并创建新的虚拟环境教程

一、什么是虚拟环境&#xff1f; 通过软件虚拟出来的开发环境&#xff0c;不是真实存在的&#xff0c;一般在多套环境开发时会用到。 二、为什么要使用虚拟环境&#xff1f; 虚拟环境为不同的项目创建不同的开发环境&#xff0c;开发环境内所有使用的工具包互不影响。比如项…

初探 YOLOv8(训练参数解析)

文章目录 1、前言2、Backbone网络3、YOLOv8模型训练代码3.1、模型大小选择3.2、训练参数设置 4、训练参数说明5、目标检测系列文章 1、前言 YOLO 因为性能强大、消耗算力较少&#xff0c;一直以来都是实时目标检测领域的主要范式。该框架被广泛用于各种实际应用&#xff0c;包…

Linux C 程序 【02】创建线程

1.开发背景 上一个篇章&#xff0c;基于 RK3568 平台的基础上&#xff0c;运行了最简单的程序&#xff0c;然而我们使用了 Linux 系统&#xff0c;系统自带的多线程特性还是比较重要的&#xff0c;这个篇章主要描述线程的创建。 2.开发需求 设计实验&#xff1a; 创建一个线程…

线性相关,无关?秩?唯一解(只有零解),无穷解(有非零解)?D=0,D≠0?

目录 线性有关无关 和 唯一解&#xff08;只有零解&#xff09;&#xff0c;无穷解&#xff08;有非零解&#xff09;之间的关系 D0&#xff0c;D≠0&#xff1f; 和 秩 的关系 串起来&#xff1a; 线性相关&#xff0c;无关&#xff1f;秩&#xff1f;唯一解&#xff08;只…

【M365运维】Outlook和Teams里不显示用户的组织架构

【问题】 由于一些误操作&#xff0c;把用户账户禁用并重新启用后&#xff0c;发现在Outlook和Teams里无法查看用户的组织结构图了。如下图所示&#xff1a; - 在Outlook 里&#xff0c;用户标签页的组织一直显示“正在加载..."&#xff0c;成员身份也是“找不到任何组。…

卸载vmware时2503,2502报错的解决办法

1.背景 windows 卸载vmware时&#xff0c;显示2503报错&#xff0c;无法完全卸载 2. 解决方案 2.1 参考安装报错2502&#xff0c;2503的处理方式 文献&#xff1a;https://blog.csdn.net/zhangvalue/article/details/80309828 2.1 步骤&#xff1a; 2.1.1 cmd 管理员打开…

O_CREAT创建函数的例子

代码&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(void) {int fd-1;char filename[]"test.txt";fdopen(filename,O_RDWR|O_CREAT|O_EXCL,S_IRWXU);if(-1fd){printf("F…

Leetcode Hot100之链表

1.相交链表 解题思路 快慢指针&#xff1a;分别求出两个链表的长度n1和n2&#xff0c;在长度较长的那个链表上&#xff0c;快指针先走n2 - n1&#xff0c;慢指针再出发&#xff0c;最后能相遇则链表相交 时间复杂度O(mn)&#xff0c;空间复杂度O(1)代码# Definition for singl…

手写SpringMVC之调度器DispatcherServlet

DispatcherServlet&#xff1a;分发、调度 根据上一节&#xff0c;已经实现了将controller的方法添加到容器中&#xff0c;而DispatcherServlet的作用就是接收来自客户端的请求&#xff0c;然后通过URI的组合&#xff0c;来找到对应的RequestMapping注解的方法&#xff0c;调用…

基于esp-idf的arm2d移植

什么是ARM2D Arm在Github上发布了一个专门针对“全体” Cortex-M处理器的2D图形加速库——Arm-2D 我们可以简单的把这个2D图形加速库理解为是一个专门针对Cortex-M处理器的标准“显卡驱动”。虽然这里的“显卡驱动”只是一个夸张的说法——似乎没有哪个Cortex-M处理器“配得上…