【源码系列】Spring事务执行原理源码


前言

由于之前写的一篇MyBatis原理源码文章点赞和收藏的用户比较多,应该是写的不错,最近正好在回顾Spring事务的源码,趁这个机会再写一篇关于事务源码,让读者能对事务有个全新的认识,同样事务的源码流程我之前也画了一张流程图,需要的自取Spring事务执行流程图,下面是processon的部分流程图
在这里插入图片描述

一、事务的原理

如果要让一个操作数据库的方法以事务方式执行,我们只需要在该方法上使用@Transaction注解,然后我们就不用管了,方法正常执行的话会自动提交,异常会自动回滚。
在这里插入图片描述
注解的属性我们可以先忽略,后面再说,其实Spring事务的实现就是基于AOP来实现的,在容器启动过程中,创建Bean时,会判断该类或者方法中是否有被@Transaction注解修饰,如果有,那么就会为该类创建代理类,也就是把Spring给我们做的事务相关操作织入到原有的逻辑。我们都知道Spring的AOP有aroundbeforeafter等,他们都会各自的通知Advisor,那么事务的Advisor是哪个呢?TransactionInterceptor,该类就是事务逻辑的Advisor

二、事务一些重要的类

老规矩,大家先跟一些重要的类混个脸熟,不要等下后面看到这些类,一脸懵逼

TransactionAttribute

存储了@Transaction注解的一些信息,隔离级别、传播特性,我们看下该对象具体有哪些属性值,通过下图我们知道该对象就是包装了用户使用@Transaction注解时指定的属性值
在这里插入图片描述

TransactionManager

Spring命名还是很规范的,见名知意,事务管理器,我们看下该类具体有些啥属性值
在这里插入图片描述

TransactionStatus

事务状态对象,该对象保存着事务运行过程中的一些属性,当前是否是一个新事物、事务保存点、是否需要回滚、还保存着挂起事务的连接资源我们看下该对象运行过程的属性值
在这里插入图片描述

TransactionDefinition

事务的定义信息,包含了执行事务的方法、方法的一些传播特效、隔离级别等
在这里插入图片描述

ConnectHolder

该对象就是包含了数据库连接的Connection对象

SuspendedResourcesHolder

被挂起的事务的资源信息
在这里插入图片描述

TransactionInfo

事务中最重要的一个类就是,该对象围绕了整个事务执行过程,它包含了数据源对象(TransactionManager)、事务个隔离级别、传播特性(TransactionAttribute)、当前事务状态对象(TransactionStatus)、还有被挂起的事务的TransactionInfo
在这里插入图片描述

TransactionSynchronizationManager

存储了一个事务执行过程中的属性和资源信息,本质就是6个ThreadLocal
在这里插入图片描述

小结

该图包含了以上所有的类,并总结了各自的作用
在这里插入图片描述

三、事务执行源码分析

源码入口

由于事务是基于AOP实现的,我们直接找到AOP的入口,DynamicAdvisedInterceptor#intercept(该类是CglibAopProxy的一个内部),先找到事务的通知类TransactionInterceptor,然后执行它的invoke方法,执行完后增强逻辑后,再执行具体的方法代码(被@Transaction注解修饰的方法)
在这里插入图片描述
在这里插入图片描述

事务增强逻辑

这里是过渡方法调用,没有多大分析的意义,直接过
在这里插入图片描述
下面这个方法可以看出整个增强逻辑做的事情,框住的每个方法比较重要,先总结该方法做了哪些事情

  1. 获取了当前事务的TransactionAttribute(上面我们说过,该对象就是将@Transactional注解的属性值那过来封装了一下)
  2. 获取了事务的管理器TransactionManager,里面包含了数据源对象和一些默认的数据库配置
  3. 创建了TransactionInfo对象,该方法里面做的事情比较多,提出去单独分析
  4. 执行了被增强的方法逻辑(那个被事务注解修饰的方法)
  5. 如果有异常,对当前事务进行处理,该方法也提到后面单独分析
  6. 不管方法执行成功还是失败,都需要清除当前的事务信息(有可能是嵌套事务,需要给之前的事务一个干净的环境去执行)
  7. 提交事务信息,之前异常处理回滚方法 completeTransactionAfterThrowing并没有对事务进行回滚,而是设置了回滚的标志位,后面我们看提交代码的时候就能看到,该方法我们也需要提出去单独分析

在这里插入图片描述

创建TransactionInfo对象

该方法主要做了两件事:

  1. 创建了当前事务的TransactionStatus,下面会具体分析
  2. 通过已经创建好的TransactionManager、TransactionAttribute、TransactionStatus 对象来对TransactionInfo进行实例化和初始化工作
    在这里插入图片描述
    可以看到实例化TransactionInfo对象后,设置了新事务的事务状态为刚创建好的TransactionStatus对象(如果该事务前面还有事务被当前事务挂起的话,那么会将老的TransactionInfo保存到oldTransactionInfo属性中,当前事务执行完后,需要恢复被挂起事务的TransactionInfo,重新设置到transactionStatus属性中)
    在这里插入图片描述
事务传播特性
  1. REQUIRED:没有事务的话,创建一个新的事务,有事务加入当前事务
  2. REQUIRES_NEW:没有事务创建新事务,有事务挂起当前事务,然后创建新事务
  3. SUPPORTS:有事务加入当前事务,没有事务以非事务执行
  4. NOT_SUPPORTED:不支持事务执行,如果当前存在事务,挂起当前事务
  5. MANDATORY:必须以事务执行,如果当前不存在事务直接抛异常
  6. NEVER:不以事务方式执行,如果当前存在事务,直接抛异常
  7. NESTED:嵌套事务,如果当前不存在事务,创建一个新的事务,存在事务,会为当前事务设置一个保存点,如果执行后续代码过程中发生异常,回滚到当前保存点的位置
获取TransactionStatus对象
  1. 获取数据源事务对象DataSourceTransactionObject,获取了当前线程中是否存在ConnectionHolder,存在的话说明之前已经存在事务了
  2. 根据事务对象判断当前是否已经存在事务了,如果存在需要对存在的事务进行处理(这里需要明白事务的各个传播特性的作用)
  3. 如果不存在事务的话,那么判断当前事务的传播特性,如果是MANDATORY直接抛异常,如果是REQUIRED、REQUIRES_NEW、NESTED,会挂起一个空事务(因为之前就没有事务所以是个空事务),然后会创建一个新的事务,剩下的传播特性的话都会创建一个空的事务
    在这里插入图片描述
处理已经存在的事务

这里就是根据事务传播特性来处理已经存在的事务
在这里插入图片描述

挂起事务

事务挂起就是将需要挂起事务的数据从TransactionSynchronizationManager各个缓存对象中取出来(其中包括了事务的名称、只读属性、隔离级别、事务活跃状态)然后将这些数据封装到SuspendedResourcesHolder对象中,然后将SuspendedResourcesHolder对象引用存到新建事务对象DefaultTransactionStatus#suspendedResources中,等新事务执行完毕后,会恢复被挂起的事务,然后将这些数据再存到TransactionSynchronizationManager对象中
在这里插入图片描述
在这里插入图片描述

恢复挂起事务

对事务挂起操作进行反操作,挂起时将事务的属性信息都封装到了SuspendedResourcesHolder对象中,我们只需要原样放回去就好
在这里插入图片描述

新建事务

创建一个新的事务,主要就是将事务的定义信息,数据源事务对象、被挂起事务信息等来创建,一个新的事务肯定会创建一个新的连接对象
在这里插入图片描述
创建Connection对象会设置当前事务的隔离级别、关闭连接的自动提交、然后将连接设置到事务数据源对象的ConnectionHolder属性中,最后将连接对象和当前线程进行绑定
在这里插入图片描述

异常回调执行逻辑

如果事务执行过程中,发生了异常,会根据异常类型来判断是否需要进行回滚,Spring默认只会对RuntimeException 和Error进行回滚操作,这也就是为啥我们在使用注解时一定要指定 @Transactional(rollbackFor = Exception.class),如果不指定是不会回滚的,不回滚就会对事务进行提交操作。
在这里插入图片描述
中间跳过了一个rollback方法,里面没做啥事,就是调用了processRollback方法,下面我们对processRollback方法进行分析

  1. 先判断当前事务是否存在保存点,如果事务传播特性为NESTED,那么会先回滚到事务保存点位置,然后清空事务的保存点信息
  2. 如果是一个全新的事务,也就是传播特性为REQUIRED(之前没有事务存在)和REQUIRED_NEW的话,就会创建一个新的事务,全新的事务没有之前事务的影响,就可以直接进行回滚操作
  3. 如果只有存在事务的话,那么就会设置一个全局的回滚标记
  4. 对事务数据的清理,有可能事务已经结束了,就需要将连接数据重置,也有可能之前还有被挂起的事务,需要恢复
    在这里插入图片描述
    在这里插入图片描述
事务数据清理工作

这一块不管事务是提交还是回滚都会执行事务数据清理,不管先前是否存在事务,当前事务已经结束了,需要将当前事务的数据从线程绑定中解除(之前存在ThreadLocal中的值),如果是一个全新事务,我需要把我的连接状态重置,然后释放连接,如果存在被当前事务挂起的线程,需要恢复挂起事务(上面已经讲过事务的挂起和恢复)。
在这里插入图片描述

清除当前事务信息

这一块代码放在finally里面,不管事务执行结果如何,都会执行。我们之前说过,如果当前事务之前还存在事务的话,会将老的事务信息从TransactionAspectSupport#transactionInfoHolder移除,并将自己的事务信息设置进去,并将老事务信息存储在当前事务的oldTransactionInfo属性中,执行到该方法,说明当前事务肯定执行完了,所以我们需要将oldTransactionInfo设置回TransactionAspectSupport#transactionInfoHolder

事务提交及后续操作

不管事务执行结果如何,都会执行该方法,会处理用户自己设置了回滚标记的逻辑(TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()),如果事务设置了全局回滚标记也会进行回滚操作,如果事务没有被设置回滚标记的话,下面就会开始处理事务的提交了

事务回滚标记处理

在这里插入图片描述

全局事务标记这一块我也不是很理解,第一个条件shouldCommitOnGlobalRollbackOnly() 默认值为false,加上取反,也就是一定会走第二个条件defStatus.isGlobalRollbackOnly(),但是该方法获取的还是当前连接的rollbackOnly属性,而上面处理回滚标记已经对该条件进行了判断处理,按道理该条件是不会进的呀

在这里插入图片描述

事务提交

如果当前事务有保存的话,代码执行到这,说明要进行真正的commit操作,之前嵌套事务设置的连接的保存点也就没有存在的必要,需要将它清除。
如果事务是一个全新的事务,那么执行到这说明事务执行完了,该执行Connection对象的commit方法啦,如果当前事务只是参与到别的事务当中,相当于做了一个空提交,当前事务只是事务中的一部分,并不是全新事务,提交与否还得取决于其它参与该事务的事务成员来决定,不管哪个参与事务的事务成员执行发生异常,都会进行回滚工作,但是提交工作必须为第一个开启事务的那个事务(第一个被@Transactional注解的方法)
在这里插入图片描述
在这里插入图片描述

四、总结

其实事务的源码还是比较好理解的,首先要知道它是基于AOP来实现的,明白事务传播特性,搞清楚各个特性的作用,执行流程主要分为三大步

  1. 事务准备:通过TransactionManager(事务管理器)、TransactionAttribute(事务注解属性的包装)、TransactionStatus(包含了连接对象、被挂起的事务等)来实例化TransacationInfo对象,
  2. 事务执行:事务代码执行,成功提交(如果当前事务不是一个全新的事务那么会做空提交,需要第一个创建事务的才提交)、失败回滚(如果当前事务有保存点,回滚到保存点,如果有被挂起事务,需要恢复挂起事务)
  3. 事务清场:归还连接对象(打开自动提交),有保存点,清除保存点,有挂起事务,恢复

观看过程中,发现我说的有不对的地方,请大佬指出,请不要让我误人子弟。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值