Spring 源码学习笔记11——Spring事务

Spring事务是基于Spring Aop的扩展
AOP的知识参见《Spring 源码学习笔记10——Spring AOP》
图片参考了https://www.processon.com/view/60f4d859e0b34d0e1b6bb40c?fromnew=1
逻辑事务和物理事务的概念来自https://wiyi.org/physical-and-logical-transactions.html

本文忽略了编程式事务,探究了基于事务注解的申明式事务

系列文章目录和关于我

一丶spring事务相关概念

事务是指访问并更新数据库中各种数据项的一个程序执行单元,在事务中的操作,要么都做修改,要么都不做。

1.事务传播级别

事务传播级别决定了事务的控制范围,这似乎不是数据库层面的概念。

1.1.required

Spring的默认传播级别,如果上下文中存在事务则加入当前事务,如果不存在事务则新建事务执行

Spring 源码学习笔记11——Spring事务

1.2.supports

如果上下文中存在事务则加入当前事务,如果没有事务则以非事务方式执行

Spring 源码学习笔记11——Spring事务

1.3.mandatory

该传播级别要求上下文中必须存在事务,否则抛出异常

Spring 源码学习笔记11——Spring事务

1.4.requires_new

该传播级别每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务。(子事务的执行结果不影响父事务的执行和回滚)但是内部调用的方法抛出异常,也会导致外部回滚

Spring 源码学习笔记11——Spring事务

1.5.not_supported

当上下文中有事务则挂起当前事务,执行完当前逻辑后再恢复上下文事务。(降低事务大小,将非核心的执行逻辑包裹执行。)

Spring 源码学习笔记11——Spring事务

1.6.never

该传播级别要求上下文中不能存在事务,否则抛出异常。

Spring 源码学习笔记11——Spring事务

1.7.nested

嵌套事务,如果上下文中存在事务则嵌套执行,如果不存在则新建事务

Spring 源码学习笔记11——Spring事务

2. 物理事务和逻辑事务

2.1物理事务

所谓物理事务指的就是Connection开启的事务。

2.2逻辑事务

在一个复杂的业务系统中,可能会调用多个service,每个service都有自己的事务(标注了@Transactional),此时我们需要根据事务传播方式(Propagation)来决定当前事务的行为(比如要挂起创建新事物,还是加入当前事务)。

我们可以认为每个注解了@Transactional的方法都是一个逻辑事务,这些逻辑事务被Spring事务管理,Spring会根据事务传播方式来决定是否开启新事务。

二丶@EnableTransactionManagement注解做了什么

在SpringBoot使用事务的时候我们通常会在配置类上面加一个 @EnableTransactionManagement注解来开启事务,其实不加也可以,SpringBoot有事务的AutoConfiguration。下来我们看下这个注解干了什么。

Spring 源码学习笔记11——Spring事务

重点在于 @Import(TransactionManagementConfigurationSelector.class)TransactionManagementConfigurationSelector实现了 ImportSelector.最终会执行 selectImports为容器注入bean。

Spring 源码学习笔记11——Spring事务

这里会注入 AutoProxyRegistrarProxyTransactionManagementConfiguration.接下来我们看些这个两个类有什么作用

1.AutoProxyRegistrar

AutoProxyRegistrar实现了 ImportBeanDefinitionRegistrar,会向容器中注入需要的 BeanDefinition,这里最终调用了 AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry),如果容器中没有名称为 org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition那么会注入 InfrastructureAdvisorAutoProxyCreator

Spring 源码学习笔记11——Spring事务

InfrastructureAdvisorAutoProxyCreatorAbstractAutoProxyCreator的子类,实现了 SmartInstantiationAwareBeanPostProcessor会在 getEarlyBeanReference提前暴露代理对象或者在bean完成实例化初始化后调用 postProcessAfterInitialization对原始的bean进行aop增强,这部分我们在Spring 源码学习笔记10——Spring AOP 已经学习过了。也就是说这里会注入一个基于动态代理实现Aop增强的一个bean后置处理器

2.ProxyTransactionManagementConfiguration

Spring 源码学习笔记11——Spring事务
  1. 注入 TransactionalEventListenerFactory 这一步是在其父类 AbstractTransactionManagementConfiguration中完成的 TransactionalEventListenerFactorySpring源码学习笔记5——注册BeanPostProcessor,初始化事件多播器,注册事件监听器中我们提到过,它会把标注 TransactionalEventListener的方法包装成一个 ApplicationListener来响应对应的事件。
    Spring 源码学习笔记11——Spring事务
    Spring 源码学习笔记11——Spring事务
  2. 注入 TransactionAttributeSource 这里注入的是 AnnotationTransactionAttributeSource,主要负责从类上,方法上,获取 @Transactional注解信息,比如抛出什么异常回滚,事务超时时间等。

看完这个类,我们基本上清楚了Spring 事务是如何实现的了,首先是通过BeanPostProcessor与Spring IOC容器结合在一起,在bean实例化初始化后调用后置处理器(如果出现循环依赖那么调用的是提前暴露对象的 getEarlyBeanReference)对bean进行Aop增强,在 AbstractAutoProxyCreatorwrapIfNecessary方法中,会获取全部的 Advisor,便会拿到注入的 BeanFactoryTransactionAttributeSourceAdvisor,然后使用 Pointcut进行过滤,然后通过 ProxyFactory选择使用JDK动态代理,还是cglib动态代理。后续调用代理对象的方法会调用到, TransactionInterceptor中的 invoke实现了事务增强的逻辑。接下来我们详细看看细节部分

三丶Spring事务源码分析

1.BeanFactoryTransactionAttributeSourceAdvisor

实现了 PointcutAdvisor接口,我们看下它的 Pointcut到底是什么,它的 Advice又是什么。

1.1 Pointcut

Spring 源码学习笔记11——Spring事务

BeanFactoryTransactionAttributeSourceAdvisor维护了一个 TransactionAttributeSourcePointcut类型的 pointcut

Spring 源码学习笔记11——Spring事务

它实现了 StaticMethodMatcherPointcut,它是一个静态方法匹配的Pointcut,这里的静态意思是不会因为入参的参数不同而改变过滤结果。我们看下具体的实现逻辑。

Spring 源码学习笔记11——Spring事务

这里的 getTransactionAttributeSource返回的是 AnnotationTransactionAttributeSource实例,这里的逻辑是只要有 TransactionAttributeSource并且可以拿到事务定义信息,那么就视为匹配,后面就会对方法进行增强。

1.2 AnnotationTransactionAttributeSource是如何获取事务定义信息的

getTransactionAttribute由其父类 AbstractFallbackTransactionAttributeSource实现,其使用一个 ConcurrentHashMap缓存方法和其对应的事务信息,如果缓存中没有那么会调用 computeTransactionAttribute方法进行获取。 computeTransactionAttribute方法会优先获取方法上面的事务注解信息,然后获取类类上面的注解信息。

Spring 源码学习笔记11——Spring事务

可以看到事务注解只能标注在public方法上面,如果是像mybatis这种生成接口动态代理类那么会拿到接口上面的注解信息。

获取注解信息调用了子类的 AnnotationTransactionAttributeSource#determineTransactionAttribute方法

Spring 源码学习笔记11——Spring事务

这里涉及到一个新的类 TransactionAnnotationParser,这是Spring根据 AnnotatedElement获取注解的接口,可以扩展此接口实现自己的事务注解,并定制事务定义信息。这些事务注解解析器在构造方法中进行了定义。

Spring 源码学习笔记11——Spring事务

可以看到Spring内置了 JTA, ejb的事务注解处理器,但是使用的是LinkedHashSet, SpringTransactionAnnotationParser会放在最前面,解析Spring的·@Transactional·注解,这也是我们最常用的事务注解, SpringTransactionAnnotationParser会拿到事务注解信息,然后把注解的属性内容包装成 RuleBasedTransactionAttribute

Spring 源码学习笔记11——Spring事务

1.3 BeanFactoryTransactionAttributeSourceAdvisor是如何获取到 Advice

它实现了 BeanFactoryAware,使用 advice持有当前通知的引用,如果没有那么从容器中根据名称拿。但是在 ProxyTransactionManagementConfiguration中直接设置 advice为容器中的 TransactionInterceptor,也就是说事务的增强逻辑定义在 TransactionInterceptor中。

2.TransactionInterceptor

TransactionInterceptor是事务拦截器,其invoke方法会在代理对象被代理方法执行的时候被回调到

Spring 源码学习笔记11——Spring事务

其中 invokeWithinTransaction是spring事务原理的关键,此方法在其父类 TransactionAspectSupport实现

2.1 利用TransactionAttributeSource获取方法或者类上的事务注解信息

事务注解决定了事务增强的代码执行逻辑,这里使用的 TransactionAttributeSource通常是是上文中我们提到的 AnnotationTransactionAttributeSource,上文中我们直到,其内部存在map缓存,如果缓存没有那么调用 TransactionAnnotationParser链式的进行解析,自然就调用到了 SpringTransactionAnnotationParser, SpringTransactionAnnotationParser反射获取注解信息包装成 TransactionAttribute对象

2.2 获取事务管理器PlatformTransactionManager

PlatformTransactionManager是spring抽象出的事务管理器接口,主要包含下面三个方法

public interface PlatformTransactionManager {
    //根据指定的传播行为返回当前活动的事务或创建新事务。
   TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
    //根据事务状态提交事务
   void commit(TransactionStatus status) throws TransactionException;
  //根据事务状态回滚事务
    void rollback(TransactionStatus status) throws TransactionException;
   }

具体实现我们后续遇到对应方法再说。

Spring 源码学习笔记11——Spring事务

这里可以看到Spring是支持 @Transactional注解value指定特定的事务管理器,但是我们实际使用中通常是没有指定的,这里就会去bean工程中获取 PlatformTransactionManager类型的bean,存在多个就会抛出异常了。这里拿到的一般是 DataSourceTransactionManager

2.3 根据事务传播级别来决定是否由必要创建事务createTransactionIfNecessary

首先调用 getTransaction方法获取一个事务,然后调用 prepareTransactionInfo封装事务的处理配置信息并绑定到当前线程

其中 getTransactionAbstractPlatformTransactionManager中是一个 final的模板方法,它首先判断是否存在一个事务,如果存在那么调用 handleExistingTransaction来创建事务,如果不存在那么根据事务传播级别来决定是否创建事务。

Spring 源码学习笔记11——Spring事务
* 存在事务的情况
Spring 源码学习笔记11——Spring事务
最后如果存在事务那么会执行下面的 handleExistingTransaction来根据事务传播级别来创建事务
2. handleExistingTransaction 首先如果传播级别是 NEVER(不支持当前事务,如果当前事务存在,则抛出异常)当前存在事务,那么抛出异常 Spring 源码学习笔记11——Spring事务 如果传播级别是 NOT_SUPPORTED(不支持当前事务;始终以非事务方式执行)那么 不会开启事务,但是支持事务同步,并且会挂起当前事务,其中 prepareTransactionStatus会初始化事务同步set,并且把当前事务的信息包装到 DefaultTransactionStatus并返回 Spring 源码学习笔记11——Spring事务 如果传播级别是 RequireNew(创建一个新事务,如果存在事务则暂停当前事务)会挂起当前事务,从ThreadLocal中解绑,然后开启新事务,并初始化TransactionSynchronization的set ![](https://img2022.cnblogs.com/blog/2605549/202208/2605549-20220828200005644-13534481.png) 如果传播级别是Nested(如果当前事务存在,则在嵌套事务中执行),首先会判断是否允许嵌套事务,如果不允许那么抛出异常,通常DataSourceTransactionManager使用savepoint来实现嵌套事务 ![](https://img2022.cnblogs.com/blog/2605549/202208/2605549-20220828200008245-813706094.png) 调用Connection#setSavepoint方法 ![](https://img2022.cnblogs.com/blog/2605549/202208/2605549-20220828180249495-2022208573.png) 最后如果是Required(支持当前事务,如果当前不存在事务那么创建一个新事务) SUPPORTS(支持当前事务;如果不存在,则以非事务方式执行) MANDATORY(支持当前事务;如果当前不存在事务,则抛出异常)` Spring 源码学习笔记11——Spring事务 都不会产生新的事务。

2.4 prepareTransactionInfo包装事务信息并绑定到ThreadLocal

Spring 源码学习笔记11——Spring事务

Spring 源码学习笔记11——Spring事务

这里会使用 oldTransactionInfo记录之前的事务信息,并且绑定当前事务信息到ThreadLocal上,这样A事务方法调用B事务方法的时,能像栈一样先进后出。在 invokeWithinTransaction调用完业务方法后,会调用 cleanupTransactionInfooldTransactionInfo重写设置到ThreadLocal中,这意味着B方法执行结束,回到了A方法的调用栈中。

2.5 回调业务逻辑 InvocationCallback#proceedWithInvocation

其实调用的是 MethodInvocation#proceed,如果当前对象存在多层代理,比如先事务代理,再基于 @AspectJ注解的方法调用时长统计,那么后面代理增强也会执行,具体逻辑在 ReflectiveMethodInvocation#proceed方法中,如果还存在其他的拦截器链那么会继续执行拦截器中的逻辑,否则直接执行我们自己的业务逻辑代码,这部分在Spring 源码学习笔记10——Spring AOP 说到过。

2.6 completeTransactionAfterThrowing 业务逻辑出现异常时的处理

Spring 源码学习笔记11——Spring事务

这里 rollbackOn取决于事务注解上面标注的在什么异常上回滚,在什么异常上不回滚,默认是在 RuntimeExceptionError上面才会回滚。

下面我们看下是如何回滚事务的

Spring 源码学习笔记11——Spring事务

Spring 源码学习笔记11——Spring事务

执行回滚 DataSourceTransactionManager是调用的 Connection#rollback方法,这里首先会获取ThreadLocal中的 TransactionSynchronization并且按照 @OrderOrdered排序然后依次调用 beforeCompletion,如果具备回滚点,那么直接回滚到保存点,如果是一个新事务,那么直接回滚,如果不是一个独立的事务,只是标记需要回滚,执行完这些后,还会回调 triggerAfterCompletion,排序然后调用 TransactionSynchronization#afterCompletion方法,然后调用 cleanupAfterCompletion清理资源,并且恢复被挂起的线程。

2.7 commitTransactionAfterReturning提交事务

具体逻辑在 processCommit中进行

Spring 源码学习笔记11——Spring事务

DataSourceTransactionManager提交事务是执行的是 Connection#commit

同样完成只会还会调用 triggerAfterCommit, triggerAfterCompletion并清理ThreadLocal中的内容,并且恢复被挂起的事务。

3.事务同步回调接口TransactionSynchronization

Spring 源码学习笔记11——Spring事务

其中 beforeCommit, beforeCompletion, aterCommitafterCompletion,只有当前事务时一个独立事务的时候才会回调,而且如果提交或者回滚的时候出现异常, beforeCompletion, afterCompletion也会被调用,我们可以通过 TransactionSynchronizationManager#registerSynchronization注册回调的逻辑。

Spring 源码学习笔记11——Spring事务

那么什么时候事务被视作时一个独立事务昵

  • 如果外部不存在一个事务,并且传播级别是 REQUIRED,REQUIRES_NEW,NESTED
  • 如果外部存在一个事务,且传播级别为 REQUIRES_NEW
  • 如果外部存在一个事务,且传播级别为嵌套事务,但是此时不是通过保存点来实现嵌套事务

Original: https://www.cnblogs.com/cuzzz/p/16633523.html
Author: Cuzzz
Title: Spring 源码学习笔记11——Spring事务

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714265/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球