2

【Spring】事务实现原理 - shanml

 1 year ago
source link: https://www.cnblogs.com/shanml/p/17762215.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在使用事务的时候需要添加@EnableTransactionManagement注解来开启事务,Spring事务底层是通过AOP来实现的,所以启用事务后,同样会向容器中注入一个代理对象创建器,AOP使用的是AnnotationAwareAspectJAutoProxyCreator,事务使用的是InfrastructureAdvisorAutoProxyCreator。

  • Advice通知:定义在切点上需要执行什么样的操作;

  • PointCut切点:定义在哪些方法上使用通知;

  • Advisor:Advice和Pointcut加起来组成了Advisor,可以看做是对切面的封装;

在使用AOP时,一般会创建一个切面,里面包含了切点和通知,事务既然基于AOP实现,所以也会有对应的通知和切点。

事务Advisor

开启事务时,还会向Spring容器中注册一个BeanFactoryTransactionAttributeSourceAdvisor,从名字上可以看出它是一个Advisor,它重点有以下几个类型的成员变量:

  • Advice(通知):传入的实际类型为TransactionInterceptor,它是事务拦截器,实现了Advice接口,这个拦截器就相当于AOP中的通知,在执行目标方法前会进行拦截,进行事务处理;

  • TransactionAttributeSourcePointcut(切点):它实现了Pointcut和MethodMatcher接口,是一个切点,目标方法是否需要被事务代理就是通过它判断的;

  • TransactionAttributeSource:传入的实际类型为AnnotationTransactionAttributeSource,用于解析事务属性相关配置信息;

Advisor由Advice和Pointcut组成,现在Advice和Pointcut都已经知道了,接下来就去看看是如何判断当前Bean是否需要进行事务代理的。

事务底层是通过AOP实现的,所以它的处理逻辑与AOP类似,启动时会注册一个后置处理器,在postProcessAfterInitialization方法中判断是否需要进行代理,逻辑与AOP一致,会获取所有的Advisor,判断是否有匹配当前Bean的Advisor,Spring会自动为事务注册Advisor(BeanFactoryTransactionAttributeSourceAdvisor),匹配的处理逻辑在TransactionAttributeSourcePointcut切点中实现,是否匹配的判断条件如下:

  1. 判断当前Bean的Class是否匹配,具体是通过ClassFilter(TransactionAttributeSourceClassFilter)的matches方法实现的,注意这里并不是判断当前Bean所在类上面是否有事务注解,这个条件主要是为了排除一些Spring认为不需要进行事务代理的类,比如某个Bean的类路径以java.开头,而我们编写的类一般不会是以java开头的,所以这个Bean就会跳过代理,对于我们编写的Bean,一般不会被这个条件过滤掉,会进行下一个条件判断;

TransactionAttributeSourceClassFilter是TransactionAttributeSourcePointcut的内部类,里面有matches方法的实现。

  1. 判断当前Bean中的方法是否与事务切点匹配,具体是通过MethodMatcher(TransactionAttributeSourcePointcut)的matches方法实现,这里会获取当前Bean的所有方法,一个个与事务切点进行匹配,匹配规则如下:

(1)从方法中获取事务注解相关的设置;
(2)从方法所在类中获取事务注解相关设置;
(2)如果方法所在的类实现了接口,还会从接口上面解析是否有事务注解相关的配置;

如果我们使用了@Transactional注解对方法或者类进行了配置,就会在这一步解析到相关内容。

如果通过以上方式中的任意一种获取到了事务相关设置,就会认为当前Bean需要进行事务代理,为其创建代理对象,实现与AOP一致,会为其创建一个AOP代理对象,只不过在执行目标方法时,Spring会通过已经设定好的事务切面进行拦截,也就是BeanFactoryTransactionAttributeSourceAdvisor中的TransactionInterceptor对进行方法拦截,而在AOP中一般是我们自己编写切面。

上面我们知道对于需要进行事务拦截的Bean,会为其创建代理对象,在执行目标方法的时候,会进入事务拦截器的处理逻辑,主要步骤如下:

  1. 获取事务管理器;
  2. 创建事务;
  3. 这里主要是向后执行拦截器链,待所有的拦截器都执行完毕之后,执行目标方法;
  4. 捕捉异常,如果出现异常进行回滚;
  5. 提交事务;
2612945-20231015214057461-1001786089.png

事务的创建

事务的创建分为两部分:

一、 获取事务

(1)首先获取事务对象,它是一个抽象方法,数据源的不同具体的实现类也不同;

以DataSourceTransactionManager为例,它主要是创建了一个数据源事务对象(DataSourceTransactionObject),然后根据数据源信息源获取当前线程绑定的ConnectionHolder对象(如果有的话会获取到否则获取为空),ConnectionHolder中存有数据库连接及事务的活跃状态,之后会将这个ConnectionHolder设置到数据源事务对象中,将数据源事务对象返回;

(2)根据上一步获取到的事务对象,判断当前线程是否存在事务,如果存在事务需要根据事务传播行为进行不同的处理;

是否存在事务的判断方式是通过当前线程是否持有数据库连接(数据源事务对象中的ConnectionHolder不为空)并且事务处于活跃状态。

  • 如果事务传播行为设是PROPAGATION_NEVER,表示不能存在事务,当前存在事务会抛出异常;
  • 如果事务的传播行为是PROPAGATION_NOT_SUPPORTED,表示以不使用事务的方式执行,如果当前存在事务,则挂起当前的事务,执行完当前逻辑后(不使用事务)再恢复挂起的事务;
  • 如果事务的传播行为是PROPAGATION_REQUIRES_NEW,表示每次执行都新建事务,如果当前存在事务需要挂起当前事务,创建一个自己的事务执行之后再恢复挂起的事务;
  • 如果事务的传播行为是PROPAGATION_NESTED,表示嵌套事务,判断是否使用保存点,如果是则使用嵌套事务,否则开启一个新事务;
  • 其他情况使用当前的事务;

(3)如果当前线程不存在事务:

  • 如果事务的传播行为是PROPAGATION_MANDATORY,它要求必须存在事务,当前不存在事务,会抛出异常;

  • 如果传播行为是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW或者PROPAGATION_NESTED,新建事务;

  • 其他情况创建一个空事务;

新建事务说明之前不存在事务,ConnectionHolder为空,此时会从数据库连接池中获取一个连接,设置到ConnectionHolder中,并将当前线程对应这个ConnectionHolder与数据源绑定(底层ThreadLocal实现),上面第(1)步中可以看到会通过数据源获取当前线程的ConnectionHolder,数据就是在这里添加的,之后就可以通过这个判断当前线程是否已经存在事务。

二、 预处理事务

主要是进行事务相关信息的封装以及事务和线程的绑定。

当执行过程中出现异常时,会进行事务回滚,回滚的处理逻辑如下:

  1. 判断事务是否设置了保存点,如果设置了将事务回滚到保存点;
  2. 如果是一个独立的新事务,直接回滚即可;
  3. 如果既没有设置保存点,也不是一个新事务,说明可能处于嵌套事务中,此时只设置回滚状态rollbackOnly为true,当它的外围事务进行提交时,如果发现回滚状态为true,外围事务则不提交;

在事务提交/回滚之后,需要根据情况清理相关的资源以及恢复被挂起的事务,主要有以下操作:

  1. 清除当前线程绑定的事务相关信息;
  2. 清除当前线程对应的ConnectionHolder与数据源的绑定关系及ConnectionHolder自身的清理;
  3. 如果挂起的事务不为空,恢复挂起的事务;

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK