当前位置: 首页 > news >正文

【老王读Spring Transaction-1】从EnableTransactionManagement顺藤摸瓜,研究@Transactional的实现原理

从EnableTransactionManagement顺藤摸瓜,研究@Transactional的实现原理

  • 前言
  • 版本约定
  • 正文
    • EnableTransactionManagement
    • ProxyTransactionManagementConfiguration——Spring 事务配置的核心类
    • TransactionInterceptor
    • TransactionAttributeSource
  • 小结

前言

Spring 对事务的封装是一个相对独立的功能,通过 spring-tx-5.3.9.jar 来进行支持。
里面的代码量也不大,我们完全可以像翻书的目录一样,浏览一下 spring-tx 的包结构和类,站在一个高的角度来审视一下 Spring 对事务的封装。

版本约定

spring-tx 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

浏览 spring-tx-5.3.9.jar ,我们大致可以挑出里面几个比较核心的类

springtxjar.png

org.springframework.transaction.annotation.Transactional  
org.springframework.transaction.annotation.EnableTransactionManagement  
org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration  

org.springframework.transaction.interceptor.TransactionAspectSupport  
org.springframework.transaction.interceptor.TransactionInterceptor    
org.springframework.transaction.interceptor.TransactionAttribute  

org.springframework.transaction.TransactionManager  
org.springframework.transaction.PlatformTransactionManager  
org.springframework.transaction.TransactionDefinition  
org.springframework.transaction.SavepointManager  

通常我们使用 Spring 的事务功能,都需要添加 @EnableTransactionManagement 来开启事务管理功能。
那就从 EnableTransactionManagement 开始看起

EnableTransactionManagement

@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

	/**
	 * 指定是否创建基于子类(CGLIB)的代理,而不是基于 jdk proxy 的代理。默认值为false。
     * 注意: 它是一个全局设置,设置为 true 将影响所有需要代理的 Spring 托管 bean,而不仅仅是那些标记为 @Transactional 的 bean。
	 */
	boolean proxyTargetClass() default false;
	
	AdviceMode mode() default AdviceMode.PROXY;
	
	int order() default Ordered.LOWEST_PRECEDENCE;

}

可以看到,@EnableTransactionManagement 注解会引入 @Import(TransactionManagementConfigurationSelector.class)

注意: proxyTargetClass 是一个全局的配置。设置为 true 将影响所有需要代理的 Spring 托管 bean,而不仅仅是那些标记为 @Transactional 的 bean。

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
    
	@Override
	protected String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY: // 默认是走这个分支
				return new String[] {AutoProxyRegistrar.class.getName(),
						ProxyTransactionManagementConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {determineTransactionAspectClass()};
			default:
				return null;
		}
	}

	private String determineTransactionAspectClass() {
		return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
				TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
				TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
	}

}

可以看到,TransactionManagementConfigurationSelector 会引入一个配置类: ProxyTransactionManagementConfiguration
ProxyTransactionManagementConfiguration 是 Spring 事务配置的核心类

ProxyTransactionManagementConfiguration——Spring 事务配置的核心类

@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    // 事务 Advisor,实现 Spring 切面事务的核心
	@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() {
		return new AnnotationTransactionAttributeSource();
	}

	// 事务拦截器
	@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;
	}

}

可以看到,这里面配置了一个 Advisor(Spring AOP 中的概念)——BeanFactoryTransactionAttributeSourceAdvisor

看到 Advisor 自然而然要想到它对应的 Pointcut 和 Advice

BeanFactoryTransactionAttributeSourceAdvisor 中设置的 Pointcut 和 Advice 分别是:

  • Pointcut: TransactionAttributeSourcePointcut
  • Advice: TransactionInterceptor

找到了 Pointcut,也就知道了它是怎么匹配 joinpoint 的。
找到了 Advice,也就知道了它在 joinpoint 上执行的 AOP 拦截逻辑是什么。(即: @Transactional 的实现逻辑)

对 Spring AOP 的 Advisor 还不清楚?建议阅读文章:Advice、Advisor、Advised都是什么接口

TransactionInterceptor

事务拦截器,是实现 Spring 事务拦截的核心类。

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();
        }
    });
}

从源码中可以看到,业务方法的执行被封装在 MethodInvocation 中,事务的处理是放在 TransactionAspectSupport#invokeWithinTransaction() 方法中实现的。

TransactionAspectSupport.png

TransactionAttributeSource

用于获取事务元数据信息的接口。事务的元数据信息就包括: 使用的事务管理器、事务隔离级别、事务传播特性、事务超时时间、针对哪些异常进行回滚等

TransactionAttributeSource 获取到的事务元数据信息主要是提供给 TransactionInterceptor 来使用的。
TransactionAttributeSource 有多种实现,其中最常用的是 AnnotationTransactionAttributeSource,它主要用来处理注解类的事务元数据,即: @Transactional

TransactionAttributeSource.png

小结

Spring 对事务的封装是一个相对独立的功能,通过 spring-tx-5.3.9.jar 来进行支持。
Spring 注解事务是通过 AOP 来实现的。具体的实现是放在 TransactionInterceptor 这个 Advice 类中。

事务注解中的事务元数据信息的解析是通过 AnnotationTransactionAttributeSource 来处理的。


了解更多源码知识,请点击视频讲解:
SpringIoC源码由浅入深 : https://edu.51cto.com/sd/68e86


如果本文对你有所帮助,欢迎点赞收藏
有关 Spring 源码方面的问题欢迎留言一起交流…

公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…

文章,请关注公众号: 老王学源码
gzh_b2.png

相关文章:

  • Caddy是什么
  • 脐带间充质干细胞
  • 取暖器遇上智能化!一张床如何分区温控,节能又好用
  • w字符编码
  • [ C++ ] STL_stack(栈)queue(队列)使用及其重要接口模拟实现
  • 【CSAPP】深入理解计算机系统 第九章 虚拟内存 动态链接 printf 17/26
  • 软文营销评论区怎样营造好的氛围?
  • Android Gradle plugin requires Java 11 问题解决
  • mysql安装,安装mysql配置教程(超级详细图解)
  • 为什么现在西红柿都“硬邦邦”的,放几个星期都不会坏?为你解答
  • java实现微信小程序获取手机号(htts接口实现)
  • 亚马逊,速卖通,国际站卖家为什么要做测评
  • Centos7搭建sftp服务器,开启SFTP上报日志
  • 模式识别课程混合式教学设计
  • 基于云原生的视频管理系统设计与实现
  • Angularjs之国际化
  • CentOS7 安装JDK
  • const let
  • HTML5新特性总结
  • JS数组方法汇总
  • PaddlePaddle-GitHub的正确打开姿势
  • Python打包系统简单入门
  • select2 取值 遍历 设置默认值
  • Vue实战(四)登录/注册页的实现
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 动态魔术使用DBMS_SQL
  • 复杂数据处理
  • 基于webpack 的 vue 多页架构
  • 让你的分享飞起来——极光推出社会化分享组件
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 试着探索高并发下的系统架构面貌
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​【已解决】npm install​卡主不动的情况
  • #pragma pack(1)
  • (13)Hive调优——动态分区导致的小文件问题
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (Java数据结构)ArrayList
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (笔试题)合法字符串
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • *p++,*(p++),*++p,(*p)++区别?
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .gitignore文件—git忽略文件
  • .NET CF命令行调试器MDbg入门(一)
  • .Net Redis的秒杀Dome和异步执行
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • /3GB和/USERVA开关
  • ;号自动换行