Spring 事务原理二

该说些什么呢?一连几天,我都沉溺在孤芳自赏的思维中无法自拔。不知道自己为什么会有这种令人不齿的表现,更不知道这颗定时炸弹何时会将人炸的粉身碎骨。好在儒派宗师曾老夫子“吾日三省吾身”的名言警醒了我。遂潜心自省,溯源头以解迷思:一开始,我的目标就是梳理知识。但最近博君一笑的怪异思维让我每日如跳梁小丑般为博君一笑而胡乱行文,后又因觉背离宗旨而主动删除。虽尚未因此遭受处罚,但这种背离宗旨的行径,着实令人不齿。为回归本心,我决定继续前一篇博文所述主题。

上一篇博客我们梳理了与事务相关的基本概念及Spring事务的基本用法,本篇博客我们将探究其基本原理。有心人可能已经发现了,其实Spring事务的实现方式与前面梳理的AOP的实现方式类似:将事务增强方法与目标方法组成一个执行链,然后由调度者依次调度执行链中的相关方法,从而达到事务控制的目标。下面就让我们从注解@ EnableTransactionManagement开始,然后一步一步向下探索吧。

该注解上面有这样一句@Import(TransactionManagementConfigurationSelector.class),其中的TransactionManagementConfigurationSelector类继承了AdviceModeImportSelector类。该类会通过selectImports(AdviceMode)方法导入两个组件ProxyTransactionManagementConfiguration和AutoProxyRegistrar。其中后者向Spring容器中注册一个InfrastructureAdvisorAutoProxyCreator 组件,该组件会通过后置处理器在对象创建以后,包装对象,然后返回一个代理对象(包含增强器)——一个包含所有拦截器链的代理对象,执行该代理对象,本质上就是执行这个拦截器链前者向容器注册一个ProxyTransactionManagementConfiguration类型的配置对象,该对象会继续向容器中注入一个事务增强器,即BeanFactoryTransactionAttributeSourceAdvisor对象(该对象会包含一个TransactionAttributeSource类型的属性,该属性的作用是用于解析事务注解,即@Transactional,故其实际类型为AnnotationTransactionAttributeSource;另外该对象还会包含一个事务拦截器,即TransactionInterceptor,它保存了事务属性信息,比如事务管理器——TransactionManager和TransactionAttributeSource等)。

下面一起来看一下InfrastructureAdvisorAutoProxyCreator类。看到这个类是不是觉得很熟悉?是的,在《Spring AOP总结二》这篇博文中,我们梳理过一个相似类型的类——AnnotationAwareAspectJAutoProxyCreator(它也继承成了ProxyProcessorSupport类,同时实现了BeanFactoryAware、SmartInstantiationAwareBeanPostProcessor接口)。下面这幅图展示的是InfrastructureAdvisorAutoProxyCreator类的结构图:

通过这幅图我们不难发现InfrastructureAdvisorAutoProxyCreator类是BeanPostProcessor接口的一个实现类,所以其必定实现了BeanPostProcessor接口中定义的两个方法:

  1. postProcessBeforeInitialization(Object bean, String beanName)
  2. postProcessAfterInitialization(Object bean, String beanName)

不过需要注意的是这两个方法的真正实现体位于AbstractAutoProxyCreator类中,关于这两个方法的具体执行逻辑,这里就不再详细描述了,想了解详情可以参见前面关于AOP的系列文章。梳理这个类的目的只有一个,希望自己能够弄清楚这个类在Spring整个事务处理中的作用:通过后置处理器在需要事务的目标对象创建以后,对该对象进行包装,然后返回一个包含增强器的代理对象

下面我们一起看一下ProxyTransactionManagementConfiguration类。这个类的源码如下面所示:

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ImportRuntimeHints(TransactionRuntimeHints.class)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource);advisor.setAdvice(transactionInterceptor);if (this.enableTx != null) {advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {// Accept protected @Transactional methods on CGLIB proxies, as of 6.0.return new AnnotationTransactionAttributeSource(false);}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}}

通过源码可以发现,该类如前面所说的那样,是一个配置类。在这个类里面我们主要关注其第一个方法,该方法的主要作用是创建一个BeanFactoryTransactionAttributeSourceAdvisor对象,并将其注册到Spring容器中。该类的继承结构如下所示:

下面拿BeanFactoryTransactionAttributeSourceAdvisor这个类与前面AOP系列文章中的InstantiationModelAwarePointcutAdvisorImpl类进行对比。不难看出它们都实现了Advisor接口。因此,理论上讲,这两个类的作用是一样的。根据前面对AOP的跟踪个人理解InstantiationModelAwarePointcutAdvisorImpl类的主要作用是保存关键数据(比如持有一个AspectJExpressionPointcut对象),并依据持有的关键数据创建对应的Advice,比如AspectJAroundAdvice、AspectJMethodBeforeAdvice、AspectJAfterAdvice、AspectJAfterReturningAdvice、AspectJAfterThrowingAdvice,等等。而这里要讲的Advisor的实现类BeanFactoryTransactionAttributeSourceAdvisor的作用也类似——整合Pointcut及Advise以方便后期使用(其持有的Advice和Pointcut对象,均是显式创建的,即new方式,具体见ProxyTransactionManagementConfigurationBeanFactoryTransactionAttributeSourceAdvisor的源码)。该对象在容器中有两个调用入口,一个调用点位于AbstractAutoProxyCreator类的postProcessBeforeInitialization()方法中,另一个调用点位于本类的postProcessAfterInitialization()方法中。上篇文章中的示例,在获取容器中的TransferService对象时,触发了BeanFactoryTransactionAttributeSourceAdvisor对象的使用,详细参见下面图片:

图中最上面箭头所指的代码就是执行的入口,通过这个方法系统可以找到适用于当前对象的Advisor对象,比如这里的一直梳理的BeanFactoryTransactionAttributeSourceAdvisor对象。接下来就是创建代理对象了(这里的操作比较复杂,这里就不再赘述,如果有兴趣的可以翻阅一下《Spring AOP总结四》这篇文章)。

接下来,让我们一起继续看看BeanFactoryTransactionAttributeSourceAdvisor源码中的TransactionAttributeSourcePointcut类型的对象。TransactionAttributeSourcePointcut类的继承体系如下图所示:

为了加深理解,我们将其与Spring AOP中的AspectJExpressionPointcut的类结构图做个对比,下面这幅图就是AspectJExpressionPointcut的类结构图(注意下面这幅图中的IntroductionAwareMethodMatcher类继承了MethodMatcher接口,BeanFactoryAware接口则继承了Aware接口):

从这两幅图可以看出:TransactionAttributeSourcePointcut和AspectJExpressionPointcut都实现了Pointcut和MethodMatcher接口。因此它们是一个方法匹配器,其中定义了方法匹配规则,也是过滤器的一种实现,其主要用于判断哪些方法需要使用当前的增强业务。这里有个问题AspectJExpressionPointcut类继承了ClassFilter,但是TransactionAttributeSourcePointcut没有,那它只通过实现MethodMatcher接口就可以完成所有过滤功能吗?不是这样的 TransactionAttributeSourcePointcut类的构造方法中有这样一行代码,如下图所示:

通过这行代码TransactionAttributeSourcePointcut对象会持有一个类型TransactionAttributeSourceClassFilter的过滤器。TransactionAttributeSourceClassFilter类的继承体系如下图所示:

所以这里很清楚了TransactionAttributeSourcePointcut会通过持有一个外部ClassFilter的方式来引入一个限制切入点或引入点与给定目标类集的匹配的筛选器,目的是用于筛选那些类需要被处理,哪些类不需要被处理。因此TransactionAttributeSourceClassFilter是过滤器,其主要作用就是筛选出合适的类,而过滤掉不合适的类。

再回到ProxyTransactionManagementConfiguration源码的第一个方法中,在创建完BeanFactoryTransactionAttributeSourceAdvisor对象后,其后面紧跟了两行属性赋值代码,具体如下图所示:

这两行代码的主要作用是为BeanFactoryTransactionAttributeSourceAdvisor对象的advice及transactionAttributeSource两个属性进行初始化。其中adevice属性的的实际类型为TransactionInterceptor,transactionAttributeSource的实际类型为AnnotationTransactionAttributeSource注意setTransactionAttributeSource()方法的本质是将AnnotationTransactionAttributeSource对象赋值给BeanFactoryTransactionAttributeSourceAdvisor对象所持有的TransactionAttributeSourcePointcut对象。下面我们就来看一下这两个类的结构图:

  • TransactionInterceptor(注意图中除了MethodInterceptor和Interceptor位于org.aopalliance.intercept包中外,其他类均位于org.springframework包中)

梳理到这里我不禁想到了Spring AOP中常见的五个通知,详情可以浏览一下《Spring AOP总结二》这篇文章。这五个通知中有三个直接实现了MethodInterceptor接口,这三个接口分别为:AspectJAfterAdvice、AspectJAroundAdvice、AspectJAfterThrowingAdvice。另外两个虽然没有直接实现这个接口,但在Spring容器启动过程中会对他们进行包装,最终包装为两个实现了MethodInterceptor接口的对象,它们分别为:MethodBeforeAdviceInterceptor和AfterReturningAdviceInterceptor。具体可以看一下《Spring AOP总结四》这篇文章。通过实现MethodInterceptor接口,最终TransactionInterceptor类有了被调用的机会(因为该类实现了MethodInterceptor接口中的invoke(MethodInvocation)方法,该方法会被最终创建的代理对象持有的链中的DynamicAdvisedInterceptor对象通过CglibMethodInvocation对象中的proceed()进行调用,进而完成对事务的控制。这部分执行逻辑同样可以在《Spring AOP总结四》这篇文章中看到)

  • AnnotationTransactionAttributeSource

下面我们看一下这个类的isCandidateClass(Class<?>)方法,该方法会对传递进来的目标class进行判断,通过调用TransactionAnnotationParser类的isCandidateClass()进行判断,其实际调用点如下图所示:

图中的Pointcut的实际类型为TransactionAttributeSourcePointcut,这里的pc.getClassFilter()会获得TransactionAttributeSourcePointcut$TransactionAttributeSourceClassFilter类型的对象,然后调用该对象的matches(Class<?>)方法,具体如下图所示:

紧接着上图所示的代码进行判断后,会直接调用TransactionAttributeSource对象上的isCandidateClass(Class<?>)方法(实际走的是AnnotationTransactionAttributeSource),注意这个TransactionAttributeSource属性的初始化是在ProxyTransactionManagementConfiguration类创建BeanFactoryTransactionAttributeSourceAdvisor对象时完成的。具体如下图所示:

AnnotationTransactionAttributeSource#isCandidateClass(Class<?>)方法会遍历本类持有的TransactionAnnotationParser集合,然后调用其上的isCandidateClass(Class<?>)方法,注意这里的TransactionAnnotationParser集合只有一个数据,即SpringTransactionAnnotationParser,下面看一下这个类的结构图:

继续看SpringTransactionAnnotationParser类中的isCandidateClass(Class<?>)方法,其源码如下所示:

public boolean isCandidateClass(Class<?> targetClass) {return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);
}

从这段代码可以看出,其主要作用就是判断目标类上是否有Transactional注解。具体如下图所示:

从这幅图可以看到前一节案例中TransferServiceImpl类(添加了@Transactional注解)在这里执行返回了true,因此这段代码的主要作用就是判断类上是否有@Transactional注解。接下来,我们继续看后面的逻辑,这就要回到下图所示的代码处了:

根据前面梳理的TransactionAttributeSourcePointcut的结构图可知,其继承了StaticMethodMatcherPointcut类,同时也继承了该类上的getMethodMatcher()方法,其最终会返回TransactionAttributeSourcePointcut对象本身。下面就不再梳理AopUtils#canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)中的代码了,其主要目的就是遍历目标类中的所有方法,即TransferServiceImpl中的所有方法(包括继承过来的),然后调用MethodMatcher接口的matches()方法,这里调的是TransactionAttributeSourcePointcut类中的matches()方法。注意:这里只会判断当前类及其中是否有方法适合TransactionAttributeSourcePointcut指定的规则,如果适合就会直接返回true。这个方法的最终调用者,即AbstractAutoProxyCreator#wrapIfNecessary(Object bean, String beanName, Object cacheKey)方法,该方法会将得到的Advisor集合继续向下传递给createProxy()方法,以完成最终的代理的创建。(跟踪过程中隐约看到,会遍历所有方法为所有方法生成链?暂不确定后续会继续跟踪)

至此,配置了事务的类的代理对象就创建完成了。接下来我们主要看一下其执行过程中必须用到的Interceptor的实现类TransactionInterceptor。该类中的invoke(MethodInvocation)方法是关键,其源码如下所示:

public Object invoke(MethodInvocation invocation) throws Throwable {// Work out the target class: may be {@code null}.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {@Override@Nullablepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}@Overridepublic Object getTarget() {return invocation.getThis();}@Overridepublic Object[] getArguments() {return invocation.getArguments();}});
}

该类会继续调用父类TransactionAspectSupport中的invokeWithinTransaction()方法。在该方法中可以看到这样几个对象:TransactionAttributeSource、TransactionAttribute、TransactionManager等。执行详情参见下图:

这里的TransactionAttributeSource的实际类型为AnnotationTransactionAttributeSrouce,接着会调用本类的determineTransactionManager()方法获取目标类上配置的与事务相关的属性,并将其包装为TransactionAttribute对象,实际类型为RuleBasedTransactionAttribute。接着获取其中配置的事务管理器(TransactionManager),其实际类型为JdbcTransactionManager。

送君千里终须一别,虽然还想继续,但终究还是想停一下!通过这篇文章我们再次回顾了AOP创建过程中寻找适当Advisor的流程,同时也了解了Spring事务创建过程中的一些重要类及其结构,最重要的是我们找到了Spring事务控制的核心代码。下篇文章我们将继续跟踪这个核心代码,以了解清楚Spring事务控制的核心原理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

条件变量、线程池以及线程的GDB调试学习笔记

目录 一、条件变量 二、线程池概念和实现 三、线程的GDB调试 一、条件变量 应用场景&#xff1a;生产者消费者问题&#xff0c;是线程同步的一种手段。 必要性&#xff1a;为了实现等待某个资源&#xff0c;让线程休眠&#xff0c;提高运行效率 使用步骤&#xff1a; 初始…

深入理解C语言(3):自定义类型详解

文章主题&#xff1a;结构体类型详解&#x1f30f;所属专栏&#xff1a;深入理解C语言&#x1f4d4;作者简介&#xff1a;更新有关深入理解C语言知识的博主一枚&#xff0c;记录分享自己对C语言的深入解读。&#x1f606;个人主页&#xff1a;[₽]的个人主页&#x1f3c4;&…

前端使用阿里Oss

前言&#xff1a;有时候为了减少宽带和服务器压力等等&#xff0c;就直接给前端操作oss实习文件上传了官方文档 安装 npm i ali-oss 因为我们是js可以使用node jdkconst OSS require(ali-oss);// 初始化OSS客户端。请将以下参数替换为您自己的配置信息。 const client new O…

Rust循环和函数

下面聊聊以下主题&#xff1a; 基于条件的分支循环函数属性测试 基于条件的分支 基于条件的分支&#xff0c;可以通过常见的 if、if else 或 if else if else 构造来完成&#xff0c;例如下面的示例&#xff1a; fn main() { let dead false; let health 48; if dead { p…

《汇编语言:基于linux环境》补码研究

刚开始我使用&#xff0c;如下命令编译&#xff0c;链接程序。 nasm -f elf64 -g -F stabs sandbox.asmld -o sandbox sandbox.ogdb sandbox当我运行 sandbox 时&#xff0c;它会正常运行&#xff0c;但 gdb 无法显示任何源代码。为什么&#xff1f;当我在 gdb 中尝试 run 时&a…

鸿蒙(ArkUI)开发:实现二级联动

场景介绍 列表的二级联动&#xff08;Cascading List&#xff09;是指根据一个列表&#xff08;一级列表&#xff09;的选择结果&#xff0c;来更新另一个列表&#xff08;二级列表&#xff09;的选项。这种联动可以使用户根据实际需求&#xff0c;快速定位到想要的选项&#…

粒子群优化算法(Particle Swarm Optimization,PSO)求解基于移动边缘计算的任务卸载与资源调度优化(提供MATLAB代码)

一、优化模型介绍 移动边缘计算的任务卸载与资源调度优化原理是通过利用配备计算资源的移动无人机来为本地资源有限的移动用户提供计算卸载机会&#xff0c;以减轻用户设备的计算负担并提高计算性能。具体原理如下&#xff1a; 任务卸载&#xff1a;移动边缘计算系统将用户的计…

Windows11搭建GPU版本PyTorch环境详细过程

Anaconda安装 https://www.anaconda.com/ Anaconda: 中文大蟒蛇&#xff0c;是一个开源的Python发行版本&#xff0c;其包含了conda、Python等180多个科学包及其依赖项。从官网下载Setup&#xff1a;点击安装&#xff0c;之后勾选上可以方便在普通命令行cmd和PowerShell中使用…

BGP同步规则

BGP同步规则&#xff1a;开启同步下&#xff0c;从IBGP收到一条路由不会传给任何EBGP邻居(实验效果IBGP邻居和EBGP邻居都不传)&#xff0c;除非从自身的IGP中也学到这条路由。目的是防止AS内部出现路由黑洞&#xff0c;向外部通告了一个本AS不可达的虚假的路由。 同步规则只影响…

qt学习:http+访问百度智能云api实现车牌识别

目录 登录到百度智能云&#xff0c;找到文字识别 完成操作指引 开通 查看车牌识别的api文档 ​编辑​编辑 查看自己应用的api key 查看回应的数据格式 编程步骤 ui界面编辑 添加模块&#xff0c;头文件和定义变量 新建两个类&#xff0c;一个图像Image类&#xff0c…

Day01-变量和数据类型课后练习(输出你最想说的一句话,定义所有基本数据类型的变量和字符串变量,用合适类型的变量存储个人信息并输出,定义圆周率PI,简答题)

参考答案博客链接跳转 文章目录 1、输出你最想说的一句话&#xff01;2、定义所有基本数据类型的变量和字符串变量3、用合适类型的变量存储个人信息并输出4、定义圆周率PI5、简答题 1、输出你最想说的一句话&#xff01; 编写步骤&#xff1a;List item定义类 Homework1&…

【嵌入式移植】5、U-Boot源码分析2—make nanopi_neo2_defconfig

U-Boot源码分析2—make nanopi_neo2_defconfig 1 概述2 nanopi_neo2_defconfig3 编译过程分析3.1 编译目标3.2 scripts_basic3.2.1 prefix src定义3.2.2 PHONY3.2.3 __build3.2.4 fixdep3.3 objscripts/kconfig 1 概述 上一章中&#xff0c;对Makefile相关源码进行了初步分析&…

从零开始:CentOS系统下搭建DNS服务器的详细教程

前言 如果你希望在CentOS系统上建立自己的DNS服务器,那么这篇文章绝对是你不容错过的宝藏指南。我们提供了详尽的步骤和实用技巧,让你能够轻松完成搭建过程。从安装必要的软件到配置区域文件,我们都将一一为你呈现。无论你的身份是运维人员,还是程序员,抑或是对网络基础设…

服务端开发小记02——Maven

这里写目录标题 Maven简介Maven在Linux下的安装Maven常用命令 Maven简介 Apache Maven Project是一个apache的开源项目&#xff0c;是用于构建和管理Java项目的工具包。 用Maven可以方便地创建项目&#xff0c;基于archetype可以创建多种类型的java项目&#xff1b;Maven仓库…

5G_RACH(一)

什么是RACH RACH 代表 Random Access Channel。这是开机时UE发给eNB的第一条消息。 为什么选择RACH &#xff1f;&#xff08;RACH 的功能是什么&#xff1f; 当你第一次听到RACH或RACH Process这个词时&#xff0c;你脑海中浮现的第一个问题是“为什么是RACH&#xff1f;”…

05 Redis之Benchmark+简单动态字符串SDS+集合的底层实现

3.8 Benchmark Redis安装完毕后会自动安装一个redis-benchmark测试工具&#xff0c;其是一个压力测试工具&#xff0c;用于测试 Redis 的性能。 src目录下可找到该工具 通过 redis-benchmark –help 命令可以查看到其用法 3.8.1 测试1 3.9 简单动态字符串SDS 无论是 Redis …

网络通信实现

【 一 】网络通信实现 【 1 】实现网络通信的四要素 本机的ip地址 子网掩码 网关的IP地址 DNS的IP地址( 域名系统) DNS服务器是指提供域名解析服务的服务器。它负责将域名转换为相应的IP地址&#xff0c;以便计算机可以通过IP地址与其他设备进行通信。 通过使用DNS服务器…

【C语言】linux内核ipoib模块 - ipoib_netdev_ops_pf结构

一、ipoib_netdev_ops_pf结构 static const struct net_device_ops ipoib_netdev_ops_pf {.ndo_init ipoib_ndo_init,.ndo_uninit ipoib_ndo_uninit,.ndo_open ipoib_open,.ndo_stop ipoib_stop,.ndo_change_mtu ipoib_change_mtu,.ndo_…

java servlet勤工助学家教管系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java servlet 勤工助学家教管系统是一套完善的java web信息管理系统 serlvetdaobean mvc 模式开发 &#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myecli…

绝地求生:【PC】开发者日志:竞技比赛地图轮换

各位玩家大家好&#xff0c;欢迎收看闲游盒本期开发者日志。 今天闲游盒想和大家分享一下2024年竞技比赛地图轮换的几项主要改动。 从第28赛季第1轮更新&#xff08;2月&#xff09;开始&#xff0c;竞技比赛的地图阵容中将包含所有8x8尺寸的地图&#xff0c;在电竞赛事中出场…