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


    前言

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

    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  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    通常我们使用 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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以看到,@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);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    可以看到,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;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    可以看到,这里面配置了一个 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();
            }
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    从源码中可以看到,业务方法的执行被封装在 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/course/30243.html


    点赞、收藏、关注不迷路…⇩↓☟⬇︎⥥…

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

  • 相关阅读:
    html, css写一个loading
    java-net-php-python-net本科生毕业设计选导师系统演示录像2019计算机毕业设计程序
    算力五力模型:一种衡量算力的综合方法
    web前端-css中最直观的反馈-伪类及伪元素选择器的使用
    谷歌硬件工程师年薪165万,苹果外籍员工222万,在大厂打工“香”吗?
    力扣 667. 优美的排列 II
    报告解读下载 | 6月《中国数据库行业分析报告》重磅发布!精彩抢先看!
    如何在Django中使用django-crontab启动定时任务、关闭任务以及关闭指定任务
    重识Nginx - 02 手把手教你编译适合自己的nginx 1.22.0
    算法手撕代码66~70
  • 原文地址:https://blog.csdn.net/wang489687009/article/details/126623617