spring事务之事务挂起和事务恢复源码解读

目录
  • 事务挂起和事务恢复源码解读
  • 事务挂起源码
    • suspend(transaction)
    • newTransactionStatus()
    • doBegin()
  • 事务恢复
    • 所以

事务挂起和事务恢复源码解读

在学习spring事务的时候,一定会涉及到一个概念,无法避免的,就是事务挂起和事务恢复

对于事务挂起和事务恢复,可以简单的描述一下,是这样的

  • 1.首先我们假设有两个类,A类和B类,两个类中的字段是一模一样的,A类表示当前事务,B类表示备份事务
  • 2.如果我开启一个事务,会把当前事务信息,存入到A类中,如果我这时候要进行事务挂起
  • 3.事务挂起:就会把A类中当前事务的信息,赋值到B类中,然后在创建一个新事务的时候,会赋值到A类中
  • 4.恢复事务:如果此时我当前事务执行完毕了,需要恢复原来的事务,就只需要将A类清空,然后将B类中的数据信息赋值到A类,此时A事务就会再次生效

我觉得可以理解为就是倒腾了一手

事务挂起源码

org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction

我们直接跳入到这个方法中来看,这个方法是在当前事务存在的时候,会进入到这个方法来处理,执行链路是这样的

org.springframework.transaction.interceptor.TransactionInterceptor#invoke
    org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
        org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
            org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
                org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction

正常的话,一个事务方法的执行是这个链路,自己debug看下即可,但是要进入到这个方法中,有一个前提,就是当前事务已存在,然后又调用了另外一个事务方法,才会进入到这里

我们以PROPAGATION_REQUIRES_NEW这个级别的传播机制为例,为什么以这个为例,因为这个传播机制,在当前事务存在的时候,是会将当前事务挂起,然后开启一个新的事务,也正好可以看下spring是如何挂起事务,并创建新事务的

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
	if (debugEnabled) {
		logger.debug("Suspending current transaction, creating new transaction with name [" +
				definition.getName() + "]");
	}
	/**
	 * 这里是挂起事务的操作,挂起事务的话,会把事务管理器中的属性设置为null
	 * ,然后将事务管理器中的属性暂时存储到suspendedResourceHolder中
	 */
	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) {
		/**
		 * 如果在开启新的事务的时候,异常了,就会在下面这个方法中,将事务恢复(和上面挂起是相对的)
		 * ,其实就是把suspendResourceHolder中的属性重新赋值到TransactionSynchronizationManager
		 */
		resumeAfterBeginException(transaction, suspendedResources, beginEx);
		throw beginEx;
	}
}

由于这个方法中,代码比较多,我就删减了一部分,只留下了propagation_requires_new这个传播机制的代码

可以看到,会先调用suspend(transaction)将当前事务挂起,然后再下面的doBegin()再开启一个新的事务,然后通过prepareSynchronization(),将事务相关信息放入到threadLocal中

suspend(transaction)

/**
* 这是挂起事务的源码
 * 所谓的事务挂起:就是将当前事务管理器中的相关属性,保存到suspendedResourceHolder中
 */
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
	/**
	 * 1.如果当前事务是active状态,就将事务挂起,挂起的操作其实也简单
	 * 将当前事务的属性信息暂存到SuspendedResourcesHolder中,然后将当前事务的属性设置为null
	 */
	if (TransactionSynchronizationManager.isSynchronizationActive()) {
		List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
		try {
			Object suspendedResources = null;
			if (transaction != null) {
				suspendedResources = doSuspend(transaction);
			}
			/**
			 * 1.1 下面就是挂起事务的操作,将事务同步管理器中的属性置为null
			 * , 然后将配置信息,存储到suspendedResources中,以便在恢复事务的时候,可以恢复
			 */
			String name = TransactionSynchronizationManager.getCurrentTransactionName();
			TransactionSynchronizationManager.setCurrentTransactionName(null);
			boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
			Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
			boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
			TransactionSynchronizationManager.setActualTransactionActive(false);
			return new SuspendedResourcesHolder(
					suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
		}
		catch (RuntimeException | Error ex) {
			/**
			 * 2.如果挂起事务失败,就需要进行回滚,就是将suspendedResourcesHolder
			 * 中的属性重新赋值到TransactionSynchronizationManager中
			 */
			// doSuspend failed - original transaction is still active...
			doResumeSynchronization(suspendedSynchronizations);
			throw ex;
		}
	}
	else if (transaction != null) {
		// Transaction active but no synchronization active.
		Object suspendedResources = doSuspend(transaction);
		return new SuspendedResourcesHolder(suspendedResources);
	}
	else {
		// Neither transaction nor synchronization active.
		return null;
	}
}

这是suspend的源码,可以看到,在1.1这个注释位置,会获取到当前事务的属性信息,然后在下面,会new SuspendedResourcesHolder(),将当前事务属性信息放入到这里面

再下面,就是一些异常的判断和处理,我们可以认为,这个方法就是把事务的属性信息存入到了SuspendedResourcesHolder对象中

newTransactionStatus()

这个方法也很重要,会把刚才创建的suspend对象,放入到DefaultTransactionStatus类中,这里我猜是为了在后面事务恢复的时候用的

doBegin()

在doBegin()方法中,主要是重新获取一个数据库连接,然后设置连接的相关信息,比如:非自动提交等

然后将连接信息存入到TransactionSynchronizationManager对象中

我们可以简单认为doBegin()就是重新开启了一个事务连接

事务恢复

前面讲的是事务挂起,下面来说事务恢复,事务恢复,就是在事务提交或者回滚的时候,会进行事务恢复的处理

这里直接贴了一张图,是事务提交和事务回滚的处理流程,最终都会调用到cleanupAfterCompletion()方法,这个方法就是事务恢复的代码

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
		status.setCompleted();
		if (status.isNewSynchronization()) {
			TransactionSynchronizationManager.clear();
		}
		if (status.isNewTransaction()) {
			doCleanupAfterCompletion(status.getTransaction());
		}
		if (status.getSuspendedResources() != null) {
			if (status.isDebug()) {
				logger.debug("Resuming suspended transaction after completion of inner transaction");
			}
			Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
			resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
		}
	}

在这个代码中,前面是一些逻辑处理,应该是对当前事务进行清除的操作,需要关注的是最后一行代码,resume()方法

protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
			throws TransactionException {

		if (resourcesHolder != null) {
			Object suspendedResources = resourcesHolder.suspendedResources;
			if (suspendedResources != null) {
				doResume(transaction, suspendedResources);
			}
			List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
			if (suspendedSynchronizations != null) {
				TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
				TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
				TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
				doResumeSynchronization(suspendedSynchronizations);
			}
		}
	}

这里可以看到,是从resourcesHolder中取一些参数赋值到TransactionSynchronizationManager中;SuspendedResourcesHolder是哪个对象呢?

就是前面事务挂起的时候,将当前事务参数信息赋值到的一个对象

所以

我们可以认为,事务挂起就是将事务赋值到一个临时对象中,事务恢复就是从临时对象中,将事务属性信息赋值到当前事务中

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java Spring AOP源码解析之事务实现原理

    目录 不用Spring管理事务? 编程式事务管理 使用PlatformTransactionManager 使用TransactionTemplate 声明式事务管理 使用@Transactional注解 源码解析 参考博客 总结 不用Spring管理事务? 让我们先来看一下不用spring管理事务时,各种框架是如何管理事务的 使用JDBC来管理事务 使用Hibernate来管理事务 业务逻辑和事务代码是耦合到一块的,并且和框架的具体api绑定了.当我们换一种框架来实现时,里面对事务控制的代码就

  • 浅谈Spring中@Transactional事务回滚及示例(附源码)

    一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除某个部门的时候,假设我们默认删除对应的成员.但是在执行的时候可能会出现这种情况,我们先删除部门,再删除成员,但是部门删除成功了,删除成员的时候出异常了.这时候我们希望如果成员删除失败了,之前删除的部门也取消删除.这种场景就可以使用@Transactional事物回滚. 二.checked异常和unc

  • 带大家深入了解Spring事务

    一.数据库事务简介 构成单一逻辑工作单元的操作集合称作事务(transaction).即使有故障,数据库系统也必须保证事务的正确执行--要么执行整个事务,要么属于该事务的操作一个也不执行.以资金转账为例,应该保证支票账户支出金额的操作和储蓄账户的存入金额的操作在同一个逻辑工作单元内完成.简言之,事务是访问并可能更新各种数据项的一个程序执行单元(unit). 二.事务的特性 数据库需要维护事务的以下四个性质: 1.原子性(Atomicity)事务是一个原子操作,由一系列动作组成.事务的原子性确保这

  • Spring源码解析之事务传播特性

    一.使用方式 可以采用Transactional,配置propagation即可. 打开org.springframework.transaction.annotation.Transactional可见默认传播特性是REQUIRED. /** * The transaction propagation type. * <p>Defaults to {@link Propagation#REQUIRED}. * @see org.springframework.transaction.inte

  • spring事务之事务挂起和事务恢复源码解读

    目录 事务挂起和事务恢复源码解读 事务挂起源码 suspend(transaction) newTransactionStatus() doBegin() 事务恢复 所以 事务挂起和事务恢复源码解读 在学习spring事务的时候,一定会涉及到一个概念,无法避免的,就是事务挂起和事务恢复 对于事务挂起和事务恢复,可以简单的描述一下,是这样的 1.首先我们假设有两个类,A类和B类,两个类中的字段是一模一样的,A类表示当前事务,B类表示备份事务 2.如果我开启一个事务,会把当前事务信息,存入到A类中,

  • spring boot使用@Async异步注解的实现原理+源码

    1.java的大部分接口的方法都是串行执行的,但是有些业务场景是不需要同步返回结果的,可以把结果直接返回,具体业务异步执行,也有些业务接口是需要并行获取数据,最后把数据聚合在统一返回给前端. 通常我们都是采用多线程的方式来实现上述业务功能,但spring 提供更优雅的方式来实现上述功能,就是@Async 异步注解,在方法上添加@Async,spring就会借助AOP,异步执行方法. 1.如何启用@Async spring boot通过@EnableAsync 注解启用@Async异步注解 实现A

  • Spring Boot 整合单机websocket的步骤 附github源码

    websocket 概念 websocket 是一个通信协议,通过单个 TCP 连接提供全双工通信.websocket 连接成功后,服务端和客户可以进行双向通信.不同于 http 通信协议需要每次由客户端发起,服务响应到客户端. websocket 相对轮询也能节约带宽,并且能实时的进行通信. 整合步骤 1. 添加 maven 依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifact

  • Spring Boot + Thymeleaf + Activiti 快速开发平台项目 附源码

    项目介绍 基于Layui的后台管理系统模板,扩展Layui原生UI样式,整合第三方开源组件,提供便捷快速的开发方式,延续LayuiAdmin的设计风格,持续完善的样式与组件的维护,基于异步Ajax的菜单构建,相对完善的多标签页,单标签页的共存,为使用者提供相对完善的开发方案,只为成为更好的轮子,项目不定时更新,建议 Star watch 一份 如果你需要无组件整合 与 示例页面 的基础框架,请前往 Pear Admin Layui 基础版本 项目结构 Pear Admin Layui │ ├─a

  • Spring源码解析之编程式事务

    一.前言 在Spring中,事务有两种实现方式: 编程式事务管理: 编程式事务管理使用TransactionTemplate可实现更细粒度的事务控制.声明式事务管理: 基于Spring AOP实现.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务. 声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单(尤其是配合spring boot自动配置,可以说是精简至极!),且大部分业务都可

  • Java Spring @Lazy延迟注入源码案例详解

    前言 有时候我们会在属性注入的时候添加@Lazy注解实现延迟注入,今天咱们通过阅读源码来分析下原因 一.一个简单的小例子 代码如下: @Service public class NormalService1 { @Autowired @Lazy private MyService myService; public void doSomething() { myService.getName(); } } 作用是为了进行延迟加载,在NormalService1进行属性注入的时候,如果MyServ

  • Spring事务执行流程及如何创建事务

    接上节内容,Spring事务执行原理通过创建一个BeanFactoryTransactionAttributeSourceAdvisor,并把TransactionInterceptor注入进去,而TransactionInterceptor实现了Advice接口.而Spring Aop在Spring中会把Advisor中的Advice转换成拦截器链,然后调用. 执行流程 获取对应事务属性,也就是获取@Transactional注解上的属性 获取TransactionManager,常用的如Da

随机推荐