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
       @Nullable
       public Object proceedWithInvocation() throws Throwable {
          return invocation.proceed();
       }
       @Override
       public Object getTarget() {
          return invocation.getThis();
       }
       @Override
       public Object[] getArguments() {
          return invocation.getArguments();
       }
    });
}

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

20240128-读书带来的影响

我本身不算是一个特别喜欢读书的人&#xff0c;更多的时候其实是为了读书而读书。在坚持每天读了一小时书之后&#xff0c;我发现自身开始慢慢有些变化。是什么时候突然有了这种感悟呢&#xff0c;是最近每周5小时左右的微信读书以及纸质书籍的阅读&#xff0c;让我体会到了读书…

C++爱好者的科目四易错点总结

科目四易错点总结 在科目四考试中&#xff0c;一部分内容是可以通过刷题快速掌握的&#xff0c;一部分内容缺因易混淆而降低我们的准确率&#xff0c;本文主要对后者进行总结&#xff0c;期待大家补充与指正。 注&#xff1a; 本文不是全部的知识点总结处 本文不是权威机构 本文…

【分布式技术专题】「探索高性能远程通信」基于Netty的分布式通信框架实现(附通信协议和代码)(上)

基于Netty的分布式通信框架实现 前提介绍回顾Dubbo分布式通信框架组成元素程序执行流程消息协议设计实现机制ChannelInboundHandlerAdapter自定义事件处理 ChannelOutboundHandlerAdapter 编(解)码处理器编码过程阶段ChannelOutboundHandlerAdapter序列化实现ChannelOutboundHa…

NLP自然语言处理的发展:从初创到人工智能的里程碑

自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;人工智能领域中备受关注的重要分支之一。它使得计算机能够理解、解释和使用人类语言。随着技术的不断发展&#xff0c;NLP经历了从初创时期到深度学习时代的巨大演变&#xff0c;推动了互联网产…

【教学类-XX -XX 】20240128名字字卡1.0(15CM正方形手工纸、黑体,说明是某个孩子的第几个名字)

作品展示&#xff1a; 15CM手工纸上一个名字&#xff0c;页眉有这个字是哪一位孩子的第X个名字的说明 背景需求&#xff1a; 去年我制作了中6班孩子的姓名卡片&#xff0c;一张A4纸上6个字&#xff0c;每张卡片大约10CM&#xff09; 【教学类-25-01】20230320 名字卡片绘画游…

使用机器学习算法检测交易中的异常行为

交易中的异常检测意味着识别交易或相关活动中的异常或意外模式。这些模式被称为异常或异常值&#xff0c;明显偏离预期规范&#xff0c;可能表明存在不规则或欺诈行为。 异常检测在各种业务中发挥着至关重要的作用&#xff0c;尤其是那些涉及金融交易、在线活动和安全敏感操作…

IMX6ULL驱动学习——通过总线设备驱动模型点亮野火开发板小灯【参考韦东山老师教程】

参考&#xff1a;【IMX6ULL驱动开发学习】11.驱动设计之面向对象_分层思想&#xff08;学习设备树过渡部分&#xff09;-CSDN博客 韦东山课程&#xff1a;LED模板驱动程序的改造_总线设备驱动模型 我使用的开发板&#xff1a;野火imx6ull pro 欢迎大家一起讨论学习 实现了总线设…

ChatGPT与文心一言:智能回复与语言准确性的较量

在当今数字化时代&#xff0c;随着人们对智能化技术的需求不断增长&#xff0c;智能回复工具也成为了日常生活中不可或缺的一部分。ChatGPT和文心一言作为两个备受瞩目的智能回复工具&#xff0c;在智能回复、语言准确性以及知识库丰富度等方面各有卓越之处。 本文将对这两者进…

JAVA编程语言单词汇总

Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称。由 James Gosling和同事们共同研发&#xff0c;并在 1995 年正式推出。后来 Sun 公司被 Oracle &#xff08;甲骨文&#xff09;公司收购&#xff0c;Java 也随之成为 Ora…

算法每日一题: 边权重均等查询 | 公共子祖先

大家好&#xff0c;我是星恒&#xff0c;今天给大家带来的是一道图里面有关公共子祖先的题目&#xff0c;理解起来简单&#xff0c;大家 题目&#xff1a;leetcode 2846 现有一棵由 n 个节点组成的无向树&#xff0c;节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n …

【Linux C | 网络编程】详细介绍 “三次握手(建立连接)、四次挥手(终止连接)、TCP状态”

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

强化合作!浪潮信息携手业界伙伴筑牢算力底座

以太平金融科技服务&#xff08;上海&#xff09;有限公司&#xff08;以下简称“太平金科”&#xff09;为例&#xff0c;在算力新型基础设施建设方面&#xff0c;该公司一直不遗余力。近日&#xff0c;该公司更携手全球领先的IT基础设施供应商浪潮信息&#xff0c;优化算力基…

静态代理IP该如何助力Facebook多账号注册运营?

在Facebook运营中&#xff0c;充分利用静态代理IP是多账号运营的关键一环。通过合理运用静态代理IP&#xff0c;不仅可以提高账号安全性&#xff0c;还能有效应对Facebook的算法和限制。以下是这些关键点&#xff0c;可以帮助你了解如何运用静态代理IP进行Facebook多账号运营&a…

如何通俗解释Docker是什么?

要想弄懂Docker&#xff0c;咱们得先从“容器化”讲起。 一、容器化技术及Docker的出现 容器化&#xff0c;它是一种轻量级、可移植的软件打包方式&#xff0c;你就想象成一个快递箱子&#xff0c;里面装着你的应用和所有需要运行的环境&#xff0c;这个箱子能在任何支持容器…

PDF标准详解(一)——PDF文档结构

已经很久没有写博客记录自己学到的一些东西了。但是在过去一年的时间中自己确实又学到了一些东西。一直攒着没有系统化成一篇篇的文章&#xff0c;所以今年的博客打算也是以去年学到的一系列内容为主。通过之前Vim系列教程的启发&#xff0c;我发现还是写一些系列文章对自己的帮…

go学习之air库的使用

首先下载air库 go install github.com/cosmtrek/air之后你需要去找到库下载的地方&#xff0c;若使用的是go mod可以使用命令 go env GOPATH找到下载库的位置 进入后&#xff0c;有bin&#xff0c;pkg目录&#xff0c;进入bin目录&#xff0c;你能看到air.exe文件 这时候将此…

NSSCTF Round#17 RE snake WP

控制流劫持可以非常快&#xff0c;当时困在中间的循环里了&#xff0c;其实一直跳到最后就行…… 运行一下发现是个贪吃蛇 联系到朝雾老师教的打飞机hit-plane那一题&#xff0c;应该通过控制流劫持直接跳转到打印flag的地方 第一个cmp分支处&#xff0c;判断轮数&#xff0c…

dataGrip连接数据库mysql和intersystems的iris

文章目录 前言创建新项目选择对应的数据库产品类型新建数据库资源连接sql命令窗体手动配置本地驱动 前言 intersystems公司的产品iris是cache的升级版本&#xff0c;目前绝大多数数据库工具都没法连接这个数据库 datagrip下载地址 https://download-cdn.jetbrains.com.cn/da…

vue3前端开发,如何引入element-plus前端框架及配置参数

vue3前端开发,如何引入element-plus前端框架及配置参数&#xff01;这是一个简单的教程&#xff0c;帮助大家快速在自己的项目中引入element-plus框架。 主要是介绍的引入流程和参数的配置情况。 如图&#xff0c;这个就是elment-plus前端框架里面的一个主按钮展示。表示我们配…

Tonka Finance 测试网活动,开启新铭文时代财富之门

Tonka Finance 是铭文赛道首个借贷市场&#xff0c;通过搭建一套铭文资产借贷质押方案&#xff0c;为铭文资产以借贷的形式释放价值、捕获流动性等方面提供了基础。作为铭文赛道最重要的基建设施之一&#xff0c;Tonka Finance 在面向市场后备受关注&#xff0c;并迅速作为铭文…