Spring源码分析总结——Spring中的声明式事务

该文章基于《Spring源码深度解析》撰写,感谢郝佳老师的奉献
Spring中的声明式事务由下面的配置文件进行管理:

<tx:annotation-drien trasaction-manager="transactionManager"/>

<bean id="transactionManager" class="org.Springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="指定的数据源">
</bean>

需要注意的是Spring中的事务只对RuntimeException方法进行回滚
Spring会使用AnnotationDrivenBeanDefinitionParser的parse方法进行解析,源代码如下:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    this.registerTransactionalEventListenerFactory(parserContext);
    String mode = element.getAttribute("mode");
    if ("aspectj".equals(mode)) {
        this.registerTransactionAspect(element, parserContext);
    } else {
        AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
    }

    return null;
}

解析时会通过mode属性进行判断(mode属性默认为proxy),如果我们需要以AspectJ的方式进行事务切入,那我们可以这样配置:

<tx:annotation-drien trasaction-manager="transactionManager" mode="aspectj"/>

我们首先对默认的proxy属性进行解析的代码进行深入分析,源码太长就不放上来了,大概的效果是:
(1)创建TransactionAttributeSource的bean然后进行注册
(2)创建TransactionInterceptor的bean然后进行注册
(3)创建TransactionAttributeSourceAdvisor的bean然后将前面两个bean注入到该bean的属性中,然后将该bean进行注册
但是在这三步之前,Spring偷偷注册了一个InfrastructureAdvisorAutoProxyCreator类型的bean,名字太长的类应该都继承了很多的父类,我们先来看看它的逻辑层次:
这里写图片描述
天,确实很大一坨,我们主要观察其中的一个接口BeanPostProcessor,这个接口保障bean在实例化时调用其postProcessAfterInitialization方法,其方法实现位于父类AbstractAutoProxyCreator类中,由于本文旨在总结,具体的代码比较长,我不放上来,实现这个接口完成了一下功能:
(1)找出指定bean对应的增强器,该功能又分为了一下几个小功能:
1.寻找候选增强器(首先获得所有对应Advisor类,然后找出对应的bean),该方法找出的bean将会用于后续步骤的织入代理
2.候选项增强器中寻找匹配项
(2)提取事务属性标签进行匹配,(首先获取对应类的方法中是否存在事务声明,然后是类,然后时接口,如果接口中仍然没有,那么到接口中的类去寻找)
(3)根据找出的增强器创建代理
完成了上面的步骤之后,我们通过已经注册到BeanFactoryTransactionAttributeSourceAdvisor的TransactionInterceptor进行增强,也就是通过该类的invoke完成事务代理的逻辑。

TransactionInterceptor支撑了整个事务功能的架构,我们针对这个类进行分析
TransactionInterceptor类层次结构
我们从类层次中可以看到TransactionInterceptor实现了MethodInterceptor接口,那么他的invoke又实现了什么效果呢:
(1)获取事务属性
(2)加载配置中配置的TransactionManager
(3)不同的事务处理方式使用不同的逻辑
Spring中存在两种事务处理方式,一种是声明式事务处理,另一种是编程式声明处理
两者的第一个区别是:编程式事务不需要事务属性
两者的第二个区别是:callbackpreferringplatformTransactionManager实现了PlatformTransactionManager接口用于暴露一个方法用于执行事务处理的回调(rollback),提交(commit),和获取事务管理器(getTransaction)。
Spring根据以上两个区别来区别到底是使用了哪种事务管理
(4)在目标方法执行前获取事务并收集事务信息
事务信息是TransactionInfo,事务属性是TransactionAttribute两者并不相同,(事务信息)TransactionInfo中不仅包含了TransactionAttribute还包括了PlatformTransactionManager以及TransactionStatus信息。
(5)执行目标方法
(6)一旦出现异常,尝试处理
请注意我们再三强调需要注意的是Spring中的事务只对RuntimeException方法进行回滚,这里我们会对其他类型的异常进行处理
(7)提交事务前的事务信息将被清除
(8)提交事务
接下来我们再来看看是怎么创建TransactionInfo(事务信息)的:
TransacationInfo的创建是在TransactionAspectSupport的createTransactionIfNecessary方法下完成的,其类结构层次如下
TransactionAspectSupport类层次
该方法的调用流程如下所示:
(1)使用DelegatingTransactionAttribute封装传入的TransactionAttribute(当前实际类型为RuleBasedTransactionAttribute,又获取事务属性生成)实例,封装的目的是为了提供更多功能
(2)获取事务(使用getTransaction来处理事务准备工作,包括事务获取以及信息构建),其详细步骤如下:
1.获取事务(有一个保存点用于允许嵌入式事务)
2.如果当前线程存在事务,则转向嵌套事务的处理
对于PROPAGATION_NESTED、PROPAGATION_REQUIRES_NEW等事务属性,都是在已经存在的事务上进行进一步的处理,Spring对这两种事务属性的处理方式如下:
PROPAGATION_NESTED(嵌入式事务)
I.首选使用保存点的方式处理异常的回滚
II.当处于JTA事务管理时处理方式与PROPAGATION_REQUIRES_NEW方式相同,一旦出现异常由Spring事务异常处理机制去完成后续操作
PROPAGATION_REQUIRES_NEW
I.将会通过suspend方法将原事务挂起(主要方式是记录原有事务的状态),在当前事务执行完毕之后将原事务还原

3.事务超时设置验证
4.事务propagationBehavior属性的验证
5.构建DefaultTransactionStatus
6.完善transaction,包括设置ConnectionHolder、隔离级别,timeout,如果是新链接则绑定到当前线程,包含当前小步骤
I.尝试获取连接(如果connectionHolder已经存在则没有必要再次获取)
II.设置隔离级别以及只读属性
III.更改默认的提交设置
IV.设置标志位,标志当前连接已被事务激活
V.设置过期时间
VI.将当前connectionHolder绑定到当前线程

7.将当前事务信息存储在当前线程

(3)构建事务信息(根据之前几个步骤获取的信息返回TransactionInfo,如果事务执行失败将通过TransactionInfo中保存的信息完成回滚等后续操作)

接着就是最重要的回滚,回滚的操作位于completeTransactionAfterThrowing方法中,但是实际上的回滚条件是异常为RuntimeException和Error,说到异常我们把异常的体系显示出来:
Throwable体系
可以看出RuntimeException只是Exception的分支,所以其实对于我们常见的Exception,该函数是并不会进行回滚的。但是Spring提供了注解的方式改变设置,例如下面的注解:

@Transactional(propagation=定义的传播规则,rollbackfor=需要回滚的异常)

下面介绍一下Spring处理回滚的大致脉络:
(1)回滚前的触发器调用
(2)回滚逻辑处理
1.根据保存点进行回滚的实现方式实际上是由数据库的连接进行的,并且需要注意的是内嵌事务异常并不会引起外部事物的回滚
2.如果为新事务那么直接回滚。
3.如果没有保存点的回滚,Spring同样使用底层数据库连接的API进行回滚
4.JTA的情况,只做回滚标志,等到提交的时候同一不提交

回滚之后的信息消除也很重要,收尾工作包含下面几个步骤:
(1)对当前事务标记已经完成,以避免重复调用
(2)如果当前事务是新的同步状态,需要将当前线程的事务信息清除
(3)如果是新事物需要做清除资源的工作(比如释放数据库连接)
(4)如果在该事务前有事务挂起,那么在当前事务结束之后需要将事务恢复

当然如果没有出现任何的异常,最后就是处理事务提交了
前面回滚逻辑的第四点,我们存在了只做回滚标记,在提交的时候统一不提交的这么一个策略,所以这是一种特殊情况
提交时由于存在内嵌事务这种情况,所以我们需要再考虑一下两个条件:
(1)当事务状态中有保存点信息的话不提交事务
因为Spring中正常处理的方式是将内嵌事物开始之前设置保存点,用于回滚,但是如果没有出现异常,内嵌事务也不会单独提交,而是交给最外层事务进行提交。
(2)当前事务非新事务也不会提交事务操作(考虑同(1))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值