【Spring事务】深入浅出Spring事务从原理到源码

  1. 什么是事务
    保证业务操作完整性的一种数据库机制 (driver 驱动)
  2. 事务特定 ACID
    A 原子性 (多次操作 要不一起成功 要不一起失败 (部分失败 savepoint))
    C 一致性 (事务开始时数据状态,事务结束是数据状态 一致 )
    I 隔离性 (多个事务不能互相影响,做到隔离)
    D 持久性 (事务操作的结果 ,永久持久化在数据库)
  3. 事务
    单机版本 和 分布式版本事务
  4. spring控制事务
    核心要点:通过AOP方式创建事务。
    方式:编程式事务、声明式事务

一个事务的demo

@Configuration
@ComponentScan("com.qxlx.tx.annotation")
@EnableTransactionManagement
public class AppConfig {

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("rootroot");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

}
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    @Transactional
    public void register(User user) {
        userDao.save(user);
    }

}
@Repository
public class UserDaoImpl implements UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void save(User user) {
        jdbcTemplate.update("insert into user(name,age) values (?,?)",user.getName(),user.getAge());
    }
}

测试类

        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = (UserService) ioc.getBean("userServiceImpl");
        User user = new User();
        user.setName("Qxlx");
        user.setAge(27);

        userService.register(user);

我们来思考,有哪些核心流程,其实对应AOP来说,1 前置处理类以及生成代理类的过程 2.在生成代理类之后,通过代理类对象执行最终方法的调用的拦截处理。
其实事务也是基于AOP进行完成功能的,所以整体也是这两个部分。

事务基础

事务属性的目的,为了更好的描述 事务的特点

隔离级别

在这里插入图片描述## 传播属性
在这里插入图片描述## 只读、超时、异常
在这里插入图片描述

前置类的初始化

在AppConfig类 引入了一个注解 @EnableTransactionManagement,我们知道一半Enable注解都是通过Import的方式引入外部类 然后去完成对应的功能。

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
	
	boolean proxyTargetClass() default false;
	
	AdviceMode mode() default AdviceMode.PROXY;

	int order() default Ordered.LOWEST_PRECEDENCE;	
}

所以实际上是引入了两个类。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

	
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
	}

AutoProxyRegistrar

在这里插入图片描述
在这里插入图片描述
查看类结果图,发现其实本质就是BPP的实现类,
在这里插入图片描述
在这里插入图片描述
所以总结一下通过@enable注解 注册一个BPP,然后BPP实例化之后,在实例化自定义bean的时候去生成代理类。剩下的代码就不过看了。

ProxyTransactionManagementConfiguration

在这里插入图片描述
这个TransactionInterceptor 拦截器很关键,在运行具体方法的时候,使用的。

运行时的核心流程

 userService.register(user);

在这里插入图片描述

return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);

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

开启事务-createTransactionIfNecessary

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

getTransaction

获取事务的状态,同时开启事务

	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
	    // new一个 DataSourceTransactionObject对象
	    // 同时从threadLocal中获取Connect datasource为key 
	    // 第一次为空
		Object transaction = doGetTransaction();
		// 是否存在事务
		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}

		 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);

			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				// 开启事务		
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
	}
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
				Connection newCon = obtainDataSource().getConnection();

				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}

			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();

			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);

			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
			if (con.getAutoCommit()) {
				txObject.setMustRestoreAutoCommit(true);
				con.setAutoCommit(false);
			}

			prepareTransactionalConnection(con, definition);
			txObject.getConnectionHolder().setTransactionActive(true);

			int timeout = determineTimeout(definition);
			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
			}

			// Bind the connection holder to the thread.
			// 绑定连接到ThreadLocal中 
			if (txObject.isNewConnectionHolder()) {
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}
		}

在这里插入图片描述

prepareTransactionInfo

进行TransactionInfo封装,
1.封装TransactionInfo对象, 将新的transactionInfo存储起来到ThreadLocal。
在这里插入图片描述

清除事务信息 cleanupTransactionInfo

将外部事务进行恢复
在这里插入图片描述

事务提交 commitTransactionAfterReturning

	txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());

事务回滚 completeTransactionAfterThrowing

原始方法 抛出异常一定回滚吗,
Exception及其子类 默认提交
RuntimeException及其子类 或者Error错误

	protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
	
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());				
			}
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
			}
		}
	}

可以发现默认抛出的异常时 RuntimeException 子类才会进行事务的回滚,或者是Error的类型。

	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

那么到这里就结束了吗,其实上面罗列的只是一个简单的流程。我们知道Spring有几大特性,其中最为复杂的传播属性是Spring特有的机制,那么他是如何完整多个事务嵌套的场景执行的呢?

传播机制原理核心原理

其实大概的流程 如果用伪代码来描述的话大概如下,假设我们执行的流程是这样。

addUser() ; 传播属性为REQUIRED 
addBlack() ; 默认传播属性 REQUIRED_NEW 
1.先执行外部addUser(); 创建连接->反射执行目标方法addUser()->接着执行addBlack()-> (1.挂起addUser事务 2.创建新事务 3.执行addBlack()-> 提交事务 4.恢复addUser事务)-> addBlack 提交事务 完成

其实问题的核心点,我们需要找到挂起事务的流程 以及恢复事务的流程 看看代码是如何实现的,基本上了解spring底层是如何完成这个处理的。

在这里插入图片描述

总结

spring事务整体的核心流程 其实就是在AOP是在IOC基础上,动态创建代理对象,事务在IOC+AOP的基础上 创建事务代理对象,完成一系列的功能。源码内容本身是比较复杂和关系比较错乱,如果想每行源码都搞清楚,那么不太现实,所以核心就是抓主干逻辑,梳理清楚核心关键点,在看细节。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Apache解析漏洞(apache_parsingCVE-2017-15715)

apache_parsing 到浏览器中访问网站 http://8.155.8.239:81/ 我们写一个木马 1.php.jpg 我们将写好的木马上传 会得到我们上传文件的路径 我们访问一下 发现上传成功 发现木马运行成功&#xff0c;接下来使用蚁剑连接我们的图片马 获取 shell 成功 CVE-2013-454 我们还是到…

CCF-GESP 等级考试 2023年9月认证C++二级真题解析

2023年9月真题 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 正确答案&#xff1a;D 解析&#xff1a;考察知识点&#xff1a;计算机基础 本题属于考察计算机基础知识。中国第一台计算机通用数字电子计算机于 1958 年 6 月由中科院计算所研制成功。那时候的逻…

linux环境使用yum方式安装nginx

linux环境使用yum方式安装nginx 一、nginx官网 二、nginx安装 点击首页的 Docs 或者 install 都可以&#xff0c;最终都是进入到Installing nginx页面 因为安装的服务器环境是linux centos 所以选择 Installation on Linux下面 packages 跳转链接 点击packages后 最终会跳转…

CS 144 check6: buiding an IP router

Lecture Notes Exercises 路由器的任务是根据路由表转发接收到的数据报&#xff1a;路由表是一系列规则&#xff0c;用于指导路由器针对任何给定的数据报应如何进行转发。 发送出什么接口。下一跳的IP地址。 这个check的工作是实现一个路由器&#xff0c;它可以为任何给定的…

Android Studio IDE环境配置

​需要安装哪些东西&#xff1a; Java jdk Java Downloads | OracleAndroid Studio 下载 Android Studio 和应用工具 - Android 开发者 | Android DevelopersAndroid Sdk 现在的Android Studio版本安装时会自动安装&#xff0c;需要注意下安装的路径Android Studio插件…

时钟周期、机器周期及指令周期是什么关系?

组成原理中&#xff0c;时钟周期、机器周期和指令周期是三个至关重要的概念&#xff0c;它们共同定义了计算机执行指令的基本时间框架。本文将对这三个周期进行详细介绍&#xff0c;并展示它们的工作原理。 一、时钟周期&#xff08;Clock Cycle&#xff09; 定义与作用 主…

YOLOv8改进,YOLOv8引入Hyper-YOLO的MANet混合聚合网络+HyperC2Net网络

摘要 理论介绍 MANet 的目标是通过多种卷积操作的协同作用,提高特征提取能力,并加强梯度流动,从而提升模型在不同层次的特征表示和语义深度。MANet 结合了三种卷积变体,通过混合使用它们来提高视觉特征的多样性和信息流动性。 HyperC2Net 的主要目标是通过超图结构对多层次…

挑战一个月基本掌握C++(第六天)了解函数,数字,数组,字符串

一 C函数 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数&#xff0c;即主函数 main() &#xff0c;所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的&#xff0c;但在逻辑上&#xff…

vue的ElMessage的css样式不生效

我使用elementplus&#xff0c;是使用的用哪个单独引入的&#xff0c;然后表单校验时候警告的css不生效&#xff0c;就是这个效果 反复看视频的引入也没发现问题&#xff0c;后来才知道需要这个引入 import { ElMessage } from "element-plus"; import element-pl…

PromptGIP:Unifying lmage Processing as Visual Prompting Question Answering

“Unifying Image Processing as Visual Prompting Question Answering” 文章提出了一种名为 PromptGIP 的通用模型&#xff0c;将图像处理任务统一为视觉提示问答范式&#xff0c;在多个图像处理任务上展现出良好性能&#xff0c;为通用图像处理提供了新的思路和方法。 confe…

Windows服务器修复SSL/TLS协议信息泄露漏洞等...

为了保证生产环境的安全, 我们会定期对服务器进行漏洞扫描, 一般情况下我们都是使用Linux服务器, 某些情况会用到Windows服务器 出现SSL/TLS协议信息泄露漏洞问题一般情况下是远程连接使用了不安全的加密算法, 需要禁用这些加密算法 修复方法 从网络中查询修复方法又很多, 大…

基于AT89C52单片机的6位电子密码锁设计

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/90166684?spm1001.2014.3001.5503 14 部分参考设计如下&#xff1a; 目 录 摘要 1 abstract 2 1 绪论 3 1.1 课题背景 3 1.2 课题的目的和意义 3 1.3 电子密码…

考前倒计时98天

2024年12月21日到2025年3月29日共有 98​ 天 一、计算机基础 思维分类特征强调学科代表理论思维&#xff08;推理思维&#xff09;推理和演绎推理数学实验思维&#xff08;证实思维&#xff09;观察和总结自然规律归纳物理学计算思维&#xff08;构造思维&#xff09;设计和构造…

力扣-图论-70【算法学习day.70】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

最新详细Gmail 注册指南以及注册谷歌账号手机号码无法验证?解决方法分享

Gmail 注册指南&#xff1a;2024 年实用教程&#xff0c;解决手机号验证难题&#xff0c;开启你的数字之旅 在当今信息时代&#xff0c;Gmail 邮箱已成为我们工作与生活中常用的数字工具&#xff0c;它不仅功能强大、界面简洁&#xff0c;还是访问 Google 服务&#xff08;如 …

【已解决】黑马点评项目jmeter高并发测试中用户数据的生成

具体实现见此篇文章的第3章 运行 test 程序后&#xff0c;生成以下用户名 以下文件名改成自己的地址 成功

范德蒙矩阵(Vandermonde 矩阵)简介:意义、用途及编程应用

参考&#xff1a; Introduction to Applied Linear Algebra – Vectors, Matrices, and Least Squares Stephen Boyd and Lieven Vandenberghe 书的网站: https://web.stanford.edu/~boyd/vmls/ Vandermonde 矩阵简介&#xff1a;意义、用途及编程应用 在数学和计算科学中&a…

数智化医院分布式计算框架融合人工智能方向初步实现与能力转换浅析

人工智能中心计算机 一、引言 1.1 研究背景与意义 近年来,人工智能(Artificial Intelligence,AI)与大数据技术的迅猛发展为医疗行业带来了前所未有的变革机遇。医疗领域积累了海量的数据,如电子病历(Electronic Medical Record,EMR)、医学影像、临床检验数据以及基因…

深度学习之超分辨率算法——SRGAN

更新版本 实现了生成对抗网络在超分辨率上的使用 更新了损失函数&#xff0c;增加先验函数 SRresnet实现 import torch import torchvision from torch import nnclass ConvBlock(nn.Module):def __init__(self, kernel_size3, stride1, n_inchannels64):super(ConvBlock…

Pytorch | 利用PI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击

Pytorch | 利用PI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集PI-FGSM介绍背景和动机算法原理算法流程 PI-FGSM代码实现PI-FGSM算法实现攻击效果 代码汇总pifgsm.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器&#xff1a; Pytorch | 从零构建AlexN…