阅读此需阅读下面这些博客先 |
---|
【Spring源码分析】Bean的元数据和一些Spring的工具 |
【Spring源码分析】BeanFactory系列接口解读 |
【Spring源码分析】执行流程之非懒加载单例Bean的实例化逻辑 |
【Spring源码分析】从源码角度去熟悉依赖注入(一) |
【Spring源码分析】从源码角度去熟悉依赖注入(二) |
【Spring源码分析】@Resource注入的源码解析 |
【Spring源码分析】循环依赖的底层源码剖析 |
【Spring源码分析】Spring的启动流程源码解析 |
【Spring源码分析】解析配置类-ConfigurationClassPostProcessor源码分析 |
【Spring源码分析】Spring之AOP底层源码解析和@Async源码解析 |
看这篇之前一定要看我的上一篇源码分析AOP的,这篇不会讲述AOP的内容,主要是分析对应的Advice。
透过源码看透Spring事务
- 一、EnableTransactionManagement 注解如何开启声明事务的?
- 二、TransactionInterceptor 源码分析
- 开启事务源码分析
- 开启事务 startTransaction 方法源码分析
- 提交事务 commitTransactionAfterReturning 方法源码分析
- 回滚 completeTransactionAfterThrowing 源码解析
- 三、总结
有些人应该是不愿意看源码的,这里先直接放我总结的图吧,也是大家想知道的Spring的事务传播机制:
一、EnableTransactionManagement 注解如何开启声明事务的?
在配置类上配置 EnableTransactionManagement 这个注解会往容器中注入俩个Bean,一个是判断Spring创建Bean时找到容器的Advisor进行AOP,一个是对应的Advisor。
可以看见上面导入了 AutoProxyRegister 和 ProxyTransactionManagementConfiguration 这俩配置类。
AutoProxyRegister 配置类会向容器内注入 InfrastructureAdvisorAutoProxyCreator 这个BeanPostProcessor,它在Bean的初始化后阶段就会判断是否进行AOP,然后通过ProxyFactory去得到对应代理对象放入单例池中,这前面都有叙述,这篇不阐述。
ProxyTransactionManagementConfiguration 会向容器内注入一个 Advisor,这个 Advisor 的 Pointcut 就是判断方法或者类上是否有 @Transaction 注解,有的话就符合要求:
二、TransactionInterceptor 源码分析
TransactionInterceptor 就是对应的 Advice,我们分析过 ProxyFactory 源码,当拿到代理对象去执行某方法的时候,就是先执行符合条件的 Advice 链,而 TransactionInterceptor 就是事务代理逻辑的一环,也是声明式事务背后的主要处理逻辑。
-
首先是获取到 @Transaction 注解的属性值然后封装成了一个 TransactionAttribute 对象;
-
然后获取到容器内的 TransactionManager 数据库管理对象,应该为 PlatformTransactionManager 类型,从容器内获取到后强转为 PlatformTransactionManager 类型。
-
然后的话在 createTransactionIfNecessary 方法中进行开启事务,这里涉及到开启事务的整个过程,Spring提供的事务传播机制也是在这里进行实现那的,非常关键的方法;
-
若没有其他AOP行为了的话,这一步就该执行目标方法了,就是去执行一堆SQL~
-
若执行目标方法这个过程抛出了异常,就尝试进行回滚,由于事务的传播机制的存在,回滚也有具体的逻辑的,不是直接回滚。
-
如果没有异常发送的话,就提交事务,也是由于传播机制的存在,提交也不是瞎鸡儿提交的,有具体的逻辑。
下面的话就对开启事务、回滚、提交这三个逻辑进行具体分析。
开启事务源码分析
就是通过注入容器的 PlatformTransactionManager 去开启对应事务(开发中如果需要手动去开启事务,也是通过注入 PlalformTransactionManager#getTransaction 去是实现的)开始事务本身是一个简单的操作,但是由于扩展了事务传播机制,这个 getTransaction 也随之变的复杂起来了,分开解析。
-
若是首次开启事务,就是不存在事务内调用了另一个@Transaction 方法。这个时候若事务的传播机制是
Mandatory
,那就直接抛出异常,这种事务传播机制不允许你首次开启事务;
-
依旧是首次开启事务,若事务传播机制是
Require、Require_New、Nested
,就会调用 startTransaction 去开启一个事务(开启事务这个方法最后解析)
-
若是已经在开启事务的基础上尝试开启事务,首先是会从ThreadLocal上下文中获取到对应数据库连接对象(这个在开启事务中会阐述,这里先知道一下),然后封装到 DataSourceTransactionObject 对象中,并设置
newConnectionHolder
这个标记为为false,表示不是自己创建的。
- 随后会根据上面那个返回的数据源对象去判断当前线程是否存在一个事务,如果存在就去执行 handleExistingTransaction 方法,去执行存在事务对应的逻辑
判断当前线程是否存在一个事务的逻辑,其实就是判断上面从上下文是否可以获取到对应的数据库连接对象
- 接下来执行对应存在事务对应的逻辑了
如果事务的传播机制是 Never
的话直接抛出异常:
如果事务传播机制是 Not_Supported
将事务进行挂起,然后正常去执行就好了(就是不受事务的传播机制所影响了)
如果事务传播机制是 Require_New
,挂起原本事务,然后新开启一个事务:
如果事务传播机制是 Nested
,则创建一个 savepoint
,正常使用此事务(如果使用的是 JTA 事务管理器就会新开启一个事务,和 Require_New 一样了都)
若是其他事务传播机制,就正常去执行,仍然是在上一个事务基础上,这里说的是 Require、Support、Mandatory
,Mandatory 是在第一次抛异常的,而这里是嵌入的 @Transaction,则仍然会继续执行。
大致流程其实说完了,现在咱细看一下开启事务和挂起事务的源码
开启事务 startTransaction 方法源码分析
doBegin 方法源码分析:
- 若当前事务没有创建数据库连接,则创建一个数据库连接对象
- 设置一些参数,如:事务隔离级别、autoCommit、readOnly等等
- 将数据库连接绑定到上下文中,供后续调用
prepareSynchronization 方法解析:
该方法就是搞了个上下文标记当前线程是开启了事务的
提交事务 commitTransactionAfterReturning 方法源码分析
- 若配置了强制回滚就会直接回滚,不会提交
- 如果配置了 savepoint,就会把savepoint设置为空,表示已经后续回滚也和这个savepoint没关系了,就咱说嵌入Transaction然后事务的传播机制是
Nested
的时候;如果这是一个新的事务,那就会进行提交,新事务就是说第一次创建的,没另一个Transaction嵌入;
- 恢复挂起的资源到当前线程中,就是将之前挂起的status重新放入到resources上下文中
回滚 completeTransactionAfterThrowing 源码解析
首先是判断抛出的异常是否符合我们配置的 rollbackFor,norollbackFor
规则,如果是不符合回滚规则的直接提交,否则走回滚逻辑:
- 如果设置了 savepoint 就回滚到 savepoint 的位置,这就是
Nested
,嵌入Transaction 时可能发生的情况
- 如果是新事务的话就直接回滚
三、总结
简单阐述这个切面 实现就是:开启事务->执行目标对象方法->提交/回滚
考虑到Spring搞了个事务传播机制,那就需要考虑传播机制带来的影响咯,默认是 Require
传播机制,理解为正常事务就好了。
直接看图吧: