Spring源码深度解析 事务总结

JDBC 方式下的事务使用示例
创建数据表结构
在这里插入图片描述
创建对应数据表的 PO
在这里插入图片描述
创建表与实体间的映射
在这里插入图片描述
在这里插入图片描述
创建数据操作接口
在这里插入图片描述
创建数据操作接口实现类
在这里插入图片描述
创建 Spring 配置文件
在这里插入图片描述
在这里插入图片描述
测试
在这里插入图片描述
默认情况下 Spring 中的 事务处理只对 RuntimeException 方法进行回滚,所以,如果
此处将 RuntimeException 替换成普通的 Exception 不会产生回滚效果

事务自定义标签
在配置文件中有这样一个配置 tx:annotation-driven/ 可以说此处配置是事务的开关
使用 Eclipse 搜索全局代码, 关键宇 annotation-drive 最终锁定类 TxNamespaceHandler
在 TxNamespaceHandler 中的 init 方法中:
在这里插入图片描述
在遇到诸如<tx:annotation-driven 为开头的配置后, Spring 都会使用 AnnotationDrivenBeanDefinitionParser 类的 parse 方法进行解析
在这里插入图片描述
在解析中存在对于 mode 属性的判断,根据代码,如果需要使用 AspectJ 的方式进行
事务切入( Spring 中的事务是以 AOP 为基础的 ),那么可以使用这样的配置
在这里插入图片描述

注册 InfrastructureAdvisorAutoProxyCreator
以默认配置为例子进行分析,进入 AopAutoProxyConfigurer 类的 configureAutoProxyCreator:
在这里插入图片描述
在这里插入图片描述

其中的两个 bean 被注册到了 个名为 advisorDef 的 bean 中, advisorDef 使用
BeanFactoryTransactionAttributeSourceAdvisor 作为其 class 属性 也就是说 BeanFactoryTransaction
AttributeSourceAdvisor 代表着当前 bean
在这里插入图片描述
上面函数configureAutoProxyCreator 中的第一句貌似很简单但却是很重要 的代码:
在这里插入图片描述
进入这个函数:
在这里插入图片描述
上面的两个函数主要目的是注册了 InfrastructureAdvisorAutoProxyCreator 类型的 bean ,那么注册这个类的目的是什么呢?查看这
个类的层次,
在这里插入图片描述
从上面的层次结构中可以看到, InfrastructureAdvisorAutoProxyCreator 间接实现了
SmartlnstantiationAwareBeanPostProcessor ,而 Smartlnstantiat onAwareB eanPostProcessm 又继承
自 InstantiationAwareBeanPostProcessor ,也就是说在 Spring 中,所有 bean 实例化时 Spring 都会
保证调用其 postProcessAfterInitialization 方法,其实现是在父类 AbstractAutoProxyCreator 类中实现
当实例化 userService 的 bean 时便会调用此方法,方法如下
在这里插入图片描述
这里实现的主要目的是对指定 bean 进行封装,当然首先要确定是否需要封装,检测及封
装的工作都委托给了 wraplfNecessary 函数进行
在这里插入图片描述
在这里插入图片描述
在wrapIfNecessary 函数中主要的工作如下
找出指定 bean 对应的增强器
根据找出的增强器创建代理

获取对应 class/method 的增强器
获取指定 bean 对应的增强器,其中包含两个关键字 :增强器与对应 也就是说在
getAdvicesAndAdvisorsForBean 函数中,不但要找出增强器,而且还需要判断增强器是否满足
要求
在这里插入图片描述
通过上面的函数, Spring 又将任务进行了拆分,分成了获取所有增强器与增强器是
否匹配两个功能点
寻找候选增强器
在 findCandidateAdvisors 函数中完成的就是获取增强器的功能
在这里插入图片描述
在这里插入图片描述
首先是通过 BeanFactoryUtils 类提供的工具方法获取所有对应 Advisor.class 的类,获取办法无非是使用 ListableBeanFactory 中提供的方法
在这里插入图片描述
而当知道增强器在容器中的 beanName 时 获取增强器已经不是问题了 在 BeanFactory 中提供了这样的方法,可以帮助快速定位对应的 bean 实例。
在这里插入图片描述

候选增强器中寻找到匹配项
当找出对应的增强器后,接来的任务就是看这些增强器是再与对应的 class 匹配了,当然
不只是 class class 内部的方法如果匹配也可以通过验证
在这里插入图片描述
在这里插入图片描述
当前分析的是对于 UserService 是否适用于此增强方法,那么当前的 advisor 就是之前
查找出来的类型为 BeanFactoryTransactionAttributeSourceAdvisor 的 bean 实例,而通过类的层次
结构又知道: BeanFactoryTransactionAttributeSourceAdvisor 间接实现了 PointcutAdvisor
因此,在 canApply 函数中的第二个 if 判断时就会通过判断,会将 Bean.Factory Transaction
AttributeSourceAdvisor 中的 getPointcut()方法返回值作为参数继续调用 canApply 方法,而
getPoint()方法返回的是 TransactionAttributeSourcePointcut 类型的实例 对于 transactionAttri bute
Source 这个属性是在解析自定义标签时注入进去的
在这里插入图片描述
使用 ransactionAttributeSourcePointcut 类型的实例作为函数参数继续跟踪 canApply
在这里插入图片描述
在这里插入图片描述

到这里不禁会有疑问,对于事务的配置不仅仅局限于在函数上配置,在
类活接口上的配置可以延续到类中的每个函数 ,那么,如果针对每个函数进行检测,在类本身
上配置的事务属性岂不是检测不到了吗? 继续探求 matcher 方法
做匹配的时候 methodMatcher.matches(method, targetClass) 会使用 TransactionAttributeSource
Pointcut 类的 matches 方法
在这里插入图片描述
此时的 tas 表示 AnnotationTransactionAttributeSource 类型, 而 AnnotationTransactionAttribute
Source 类型的 getTransactionAttribute 方法如下:
在这里插入图片描述
在 getTransactionAttribute 函数中并没有找到想要的代码,这里是指常规的一贯的
套路。尝试从缓存加载,如果对应信息没有被缓存的话,工作又委托给了 computeTransaction
Attribute 函数,在 computeTransactionAttribute 函数中终于看到了事务标签的提取过程
提取事务标签
在这里插入图片描述
对于函数 computeTransactionAttribute 中的逻辑与我们所认识的规则并无差别,但是上面函数中并没
由真正的去做搜寻事务属性的逻辑,而是搭建了个执行框架,将搜寻事务属性的任务委托给了
findTransactionAttribute 方法去执行
在这里插入图片描述
this.annotationParsers 是在当前类 AnnotationTransactionAttributeSource 初始化的时候初
始化的 其中的值被加入了 SpringTransactionAnnotationParser 也就是当进行属性获取的时
候其实是使用 SpringTransactionAnnotationParser 类的 parseTransactionAnnotation 方法进行
解析的
在这里插入图片描述
首先会判断当前的类是否含有Transactional 注解,这是事务属性的基础 ,当然如果有的话会继续调用 parseTransactionAnnotation
方法解析详细的属性
在这里插入图片描述
在这里插入图片描述

事务增强器
Transactionlnterceptor 支撑着整个事务功能的架构,逻辑还是相对复杂的,那么现在
来分析此拦截器是如何实现事务特性的 Transactionlnterceptor 类继承自 Methodlnterceptor ,所
以调用该类是从其 invoke 方法开始的, 首先预览下这个方法在这里插入图片描述
在这里插入图片描述
对于声明式的事务处理主要有以下几个步骤
获取事务的属性
加载配置中配置的 TransactionManager
不同的事务处理方式使用不同的逻辑
在目标方法执行前获取事务并收集事务信息
执行目标方法
一旦出现异常,尝试异常处理
提交事务前的事务信息清除
提交事务

创建事务
在这里插入图片描述
在这里插入图片描述
对于 createTransactionltNecessar 函数主要做了这样几件事情
使用 DelegatingTransactionAttribute 封装传人的 TransactionAttribute 实例
获取事务
构建事务信息

获取事务
在这里插入图片描述
在这里插入图片描述

事务的准备工作都包括哪些
获取事务
创建对应的事务实例,这里使用的是 DataSourceTransactionManager 中的 doGetTransaction
方法 ,创建基于 JDBC 的事务实例 如果当前线程中存在关于 dataSource 的连接,那么直接使
用 这里有一个对保存点的设置 ,是否开启允许保存点取决于是否设置了允许嵌入式事务
在这里插入图片描述
如果当先线程存在事务 则转向嵌套事务的处理
事务超时设置验证
事务 propagationBehavior 属性的设置验证
构建 DefaultTransactionStatus
完善 transaction ,包括设置 ConnectionHolder 隔离级别、 timeout ,如果是新连接,则
绑定到当前线程
对于一些隔离级别、 timeout 等功能的设置并不是由 Spring 完成的,而是委托给底层的
数据库连接去做的,而对于数据库连接的设置就是在 doBegin 函数中处理的
在这里插入图片描述
在这里插入图片描述

在获取数据库连接的同时 一些必要的设置也是需要同步设置的
尝试获取连接
设置隔离级别以及只读标识
更改默认的提交设置
设置标志位,标识当前连接已经被事务激活
设置过期时间
将 connectionHolder 绑定到当前线程
设置隔离级别的 prepareConnectionForTransaction 函数用于负责对底层数据库连接的设置,
当然 只是包含只读标识和隔离级别的设置 由于强大的日志及异常处理 显得函数代码量比
较大,但是单从业务角度去看,关键代码其实是不多的
在这里插入图片描述
将事务信息记录在当前线程中
在这里插入图片描述

处理已经存在的事务
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
函数中对已经存在的事务处理考虑两种情况
PROPAGATION_REQUIRES_NEW 表示当前方法必须在它自己的事务里运行,一个新
的事务将被启动,而如果有一个事务正在运行的话,则在这个方法运行期间被挂起 而
Spring 中对于此种传播方式的处理与新事务建立最大的不同点在于使用 suspend 方法将
原事务挂起 将信息挂起的目的当然是为了在当前事务执行完毕后在将原事务还原
PROPAGATION_NESTED 表示如果当前正有一个事务在运行中,则该方法应该运行在
一个嵌套的事务中,被嵌套的事务可以独立于封装事务进行提交或者回滚,如果封装事
务不存在,行为就像 PROPAGATION_REQUIRES_NEW 对于嵌入式事务的处理, Spring
中主妥考虑了两种方式的处理
Spring 中允许嵌入事务的时候,则首选设直保存点的方式作为异常处理的回滚
对于其他方式,比如 JTA 无法使用保存点的方式,那么处理方式与 PROPAGATION_
REQUIRES_NEW 相同 而一旦出现异常 则由 Spring 的事务异常处理机制去完成
后续操作
对于挂起操作的主要目的是记录原有事务的状态,以便于后续操作对事务的恢复
在这里插入图片描述
准备事务信息
当已经建立事务连接并完成了事务信息的提取后,我们需要将所有的事务信息统一记录在
Transactionlnfo 类型的实例中,这个实例包含了目标方法开始前的所有状态信息,一旦事务执
行失败, Spring 会通过 Transactionlnfo 类型的实例中的信息来进行回滚等后续工作
在这里插入图片描述

回滚处理
在这里插入图片描述
在这里插入图片描述
回滚条件
默认情况下 Spring 中的事务异常处理机制只对 RuntimeException 和 Error 两种情
况感兴趣,当然可以通过扩展来改变,不过,最常用的还是使用事务提供的属性设置
利用注解方式的使用,例如:
在这里插入图片描述
回滚处理
在这里插入图片描述
在这里插入图片描述
Spring 中对于回滚处理的大致脉络如下
首先是自定义触发器的调用 ,包括在回滚前、完成回滚后的调用,当然完成回滚包括
正常回滚与回滚过程中出现异常,向定义的触发器会根据这些信息作进一步处理,而对于触
发器的注册,常见是在回调过程中通过 TransactionSynchronizationManager 类中的静态方法直
接注册
在这里插入图片描述
除了触发监听函数外,就是真正的回滚逻辑处理了
当之前已经保存的 事务信息 中有保存点信息的时候,使用保存点信息进行回滚。常用于
嵌入式事务,对于嵌入式的事务的处理,内嵌的事务异常并不会引起外部事务的回滚
根据保存点回滚的实现方式其实是根据底层的数据库连接进行的
在这里插入图片描述
这里使用的是 JDBC 的方式进行数据库连接,那么 getSavepointManager()函数返回的是
JdbcTransactionObjectSupport ,也就是说上面函数会调用 JdbcTransactionObjectSupport 中的
rollbackToSavepoint 方法
在这里插入图片描述
当之前已经保存的事务信息中的事务为新事务,那么直接回滚 常用于单独事务的处理
对于没有保存点的回滚 Spring 同样是使用底层数据库连接提供的 API 来操作的 由于
使用的是 DataSourceTransactionManager ,那么 doRollback 函数会使用此类中的实现:
在这里插入图片描述
当前事务信息中表明是存在事务的,又不属于以上两种情况,多数用于 JTA ,只做回
滚标识,等到提交的时候统一不提交

回滚后的信息清除
对于回滚逻辑执行结束后,无论回滚是有成功,都必须要做的事情就是事务结束后的收尾工作
在这里插入图片描述
事务处理的收尾处理工作包括如下内容
设置状态是对事务信息作完成标识以避免重复调用
如果当前事务是新的同步状态,需要将绑定到当前线程的事务信息清除
如果是新事务需要做些清除资源的工作
在这里插入图片描述
在这里插入图片描述
如果在事务执行前有事务挂起,那么当前事务执行结束后需要将挂起事务恢复
在这里插入图片描述

事务提交
在这里插入图片描述
在真正的数据提交之前 还需要做个判断,当某个事务既没有保存点又不是新事务, Spring 对它的处理方式只是设置
一个回滚标识 这个回滚标识在这里就会派上用场了 ,主要应用场景如下
某个事务是另一个事务的嵌入事务 但是, 这些事务又不在 Spring 的管理范围 内, 或者无
法设置保存点 那么 Spring 会通过设置回滚标识的方式来禁止提交 首先当某个嵌入事务发生
回滚的时候会设置凹滚标识,而等到外部事务提交时, 一旦判断出当前事务流被设置了回滚标
识 则由外部事务来统一进行整体事务的回滚
所以,当事务没有被异常捕获的时候也并不意味着一定会执行提交的过程
在这里插入图片描述
在这里插入图片描述
而当事务执行一切都正常的时候,便可以真正地进入提交流程了
在这里插入图片描述
在这里插入图片描述
在提交过程中也并不是直接提交的 而是考虑了诸多的方面 符合提交的条件如下
当事务状态中有保存点信息的话便不会去提交事务
当事务非新事务的时候也不会去执行提交事务操作
此条件主要考虑内嵌事务的情况,对于内嵌事务,在 Spring 中正常的处理方式是将内嵌事
务开始之前设置保存点, 一旦内嵌事务出现异常便根据保存点信息进行回滚,但是如果没有出
现异常 ,内嵌事务并不会单独提交, 而是根据事务流由最外层事务负责提交, 所以如果当前存
在保存点信息便不是最外层事务 不做保存操作,对于是否是新事务的判断也是基于此考虑
如果程序流通过了事务的层层把关,最后顺利地进入了提交流程, 那么同样, Spring 会将
事务提交的操作引导至底层数据库连接的 API 进行事务提交
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值