Spring 事务原理二

news2024/11/21 2:26:15

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

上一篇博客我们梳理了与事务相关的基本概念及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.coloradmin.cn/o/1416819.html

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

相关文章

springIoc以及注解的使用

注解 注解的定义 注解&#xff08;Annotation&#xff09;是一种在 Java 程序中以元数据的形式对代码进行标记和说明的机制。它可以被添加到类、方法、字段、参数等程序元素上&#xff0c;用于提供额外的信息和指示。 也就是说注解是一种标记 注解怎么生效呢&#xff1f; 通…

Element-Plus如何实现表单校验和表单重置

一&#xff1a;页面布局介绍&#xff1a; 这是我刚刚用基于vue3element-plus写好的一个部门管理的页面 基本的增删改查已经写好&#xff0c;下面我只提供页面的template和style的代码&#xff1a; template <template><el-card class"box-card"><…

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

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

BGP:04 fake-as

使用 fake-as 可以将本地真实的 AS 编号隐藏&#xff0c;其他 AS 内的对等体在指定本端对等体所在的AS 编号时&#xff0c;应该设置成这个伪AS 编号。 这是实验拓扑&#xff0c;IBGP EBGP 邻居都使用物理接口来建立 基本配置&#xff1a; R1: sys sysname R1 int loo0 ip add…

网络原理,网络通信以及网络协议

​​​​&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录专栏&#xff1a;网络原理,网络通信以及网络协议 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录 网络原理概念网络通信局域网LAN广域网WAN 网络通信IP地址端口号…

第17节-高质量简历写作求职通关-投递反馈

&#xff08;点击即可收听&#xff09; 投递跟进和感谢信 如果对一家公司特别心仪&#xff0c;但是投递简历后一直得不到回复怎么办&#xff1f; 面试之后觉得自己没有表现好怎么办&#xff1f; 面试完几天了&#xff0c;依然没有得到回应怎么办&#xff1f; 这个时候你需要写一…

OkHttp完全解读

一&#xff0c;概述 OkHttp作为android非常流行的网络框架&#xff0c;笔者认为有必要剖析此框架实现原理&#xff0c;抽取并理解此框架优秀的设计模式。OkHttp有几个重要的作用&#xff0c;如桥接、缓存、连接复用等&#xff0c;本文笔者将从使用出发&#xff0c;解读源码&am…

sqli-labs靶场第七关

7、第七关 id1 --单引号报错,id1" --双引号不报错,可以判断是单引号闭合 id1) --也报错&#xff0c;尝试两个括号闭合&#xff0c;id1)) --不报错 接下来用脚本爆库 import stringimport requestsnumbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] letters2 list(string.ascii_…

二、Gradle 与 Idea 整合

这里写自定义目录标题 1、Groovy简介2、Groovy 安装3、创建 Groovy 项目4、Groovy 基本语法 1、Groovy简介 详细了解请参考&#xff1a;http://www.groovy-lang.org/documentation.html 2、Groovy 安装 下载后解压到本地 验证&#xff1a; groovy的安装情况 3、创建 Groo…

231. Power of Two(2 的幂)

题目描述 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2 x n 2^x n2x&#xff0c;则认为 n 是 2 的幂次方。 问题分析 题目要求的是给定一个数判断…

[ESP32 IDF] wifi 的应用

目录 背景知识 wifi的基本连接使用 WiFi篇—— WiFi两种模式文章中二、WiFi 的启动&#xff08;STA 及 AP 模式&#xff09; 输出现象 通过websocket控制LED 实践验证 实验现象 背景知识 WIFI是ESP32非常重要的一个功能&#xff0c;想要使用一下IDF的API实现将ESP32连…

Golang Playground: 轻松提升你的技能

探索、实验和学习 Go 语言 Golang Playground 是一个在线工具&#xff0c;允许用户在方便且友好的环境中实验、练习和提升他们的编码技能。无论是初学者还是开发人员&#xff0c;Golang Playground 都提供了一个无需本地安装的环境&#xff0c;可以轻松编写、编译和执行 Go 代…

第四篇:怎么写express的路由(接口+请求)

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 引言&#xff1a; &#x1f4…

防御保护第六天笔记

一、防火墙的用户认证 用户、行为、流量 --- 上网行为管理三要素 防火墙管理员登录认证的作用有两点&#xff1a;检验身份的合法性&#xff0c;划分身份权限 用户认证 --- 上网行为管理的一部分 用户认证分类有以下三类&#xff1a; 1、上网用户认证 --- 三层认证 --- 所有的…

k8s 进阶实战笔记 | Scheduler 调度策略总结

文章目录 Scheduler 调度策略总结调度原理和过程调度策略nodeSelect亲和性和反亲和性NodeAffinify亲和验证PodAffinity 亲和验证PodAntiAffinity 反亲和验证污点与容忍跳过 Scheduler 调度策略 调度策略场景总结 Scheduler 调度策略总结 调度原理和过程 Scheduler 一直监听着…

拼多多砍价群2024年最新群聊分享

分享最新拼多多现金助力互助微信群138个&#xff0c;井然有序打发时间&#xff0c;拼多多互点不求人&#xff0c;#拼多多互助群#一起来相互助力&#xff01; ​拼多多互助砍价群免费助力互助群&#xff0c;拼多多助力群免费微信&#xff0c;识别下方二维码进群。拼多多助力群免…

仅需这条指令解决 sudo 报错或将用户添加到 sudoers

解决 sudo 报错或将用户添加到 sudoers 仅需这条指令 既然找到了这里&#xff0c;我只想通过查找了整整一天得到的经验和教训告诉你答案&#xff0c;不需要 nano、vim 这类的编译器&#xff0c;也不需要 chmod 更改 /etc/sudoers 文件只读权限&#xff0c;只需要控制台终端在 …

文心一言 VS ChatGPT :谁是更好的选择?

前言 目前各种大模型、人工智能相关内容覆盖了朋友圈已经各种媒体平台&#xff0c;对于Ai目前来看只能说各有千秋。GPT的算法迭代是最先进的&#xff0c;但是它毕竟属于国外产品&#xff0c;有着网络限制、注册限制、会员费高昂等弊端&#xff0c;难以让国内用户享受。文心一言…

【Redis】关于它为什么快?使用场景?以及使用方式?为何引入多线程?

目录 1.既然redis那么快&#xff0c;为什么不用它做主数据库&#xff0c;只用它做缓存&#xff1f; 2.Redis 一般在什么场合下使用&#xff1f; 3.redis为什么这么快&#xff1f; 4.Redis为什么要引入了多线程&#xff1f; 1.既然redis那么快&#xff0c;为什么不用它做主数据…

【论文解读】Object Goal Navigation usingGoal-Oriented Semantic Exploration

论文&#xff1a;https://devendrachaplot.github.io/papers/semantic-exploration.pdf 代码&#xff1a;https://github.com/devendrachaplot/Object-Goal-Navigation 项目&#xff1a; Object Goal Navigation using Goal-Oriented Semantic Exploration example&#xff1…