zoukankan      html  css  js  c++  java
  • 理解 Transactional 的工作原理

    本文由 简悦 SimpRead 转码, 原文地址 www.kailing.pub 引言

    写这篇博文有个来由,是为了解决博主遇到的多数据源的事务问题(用不了 JTA),所以深入到 spring-tx 的源码去学习了一番,非常有收获,最后博主的分布式事务问题也迎刃而解了,这个文章算个开篇,关于如何处理多数据源事务,待下文分解。本文涉及到的技术包含 spring aop 的使用、spring bean 生命周期等,如果能够真正理解 Transactional 的工作原理,对排查事务相关的问题有非常大的帮助。

    spring-tx 版本:5.0.2

    工作机制简述

    先来看一张官方的事务简图:

    spring 定义了 @Transactional 注解,基于 AbstractBeanFactoryPointcutAdvisor、StaticMethodMatcherPointcut、MethodInterceptor 的 aop 编程模式,增强了添加 @Transactional 注解的方法。同时抽象了事务行为为 PlatformTransactionManager(事务管理器)、TransactionStatus(事务状态)、TransactionDefinition(事务定义) 等形态。最终将事务的开启、提交、回滚等逻辑嵌入到被增强的方法的前后,完成统一的事务模型管理。

    事务 aop 核心类释义

    @Transactional

    事务注解,用于定位 aop 的切入点,事务注解里包含了完整事务的所有基本属性,常见的属性如:

    • transactionManager:事务管理器
    • propagation:传播行为定义,枚举类型,是 spring 独有的事务行为设计,默认为 PROPAGATION_REQUIRED(支持当前事务,不存在则新建)
    • isolation:隔离级别,对应数据库的隔离级别实现,mysql 默认的隔离级别是 read-committed
    • timeout:超时时间,默认使用数据库的超时,mysql 默认的事务等待超时为 5 分钟
    • readOnly:是否只读,默认是 false
    • rollbackFor:异常回滚列表,默认的是 RuntimeException 异常回滚

    TransactionAttribute

    事务属性抽象接口类,承载了 @Transactional 注解里的所有属性,实现类的继承关系如下类结构图,这个实例在被注解解析器创建好后,会在事务上下文中传递

    SpringTransactionAnnotationParser

    见名知意,这个类是 spring 的事务注解解析器,实现自 TransactionAnnotationParser 接口,是 spring 管理的事务解析器,用于解析 @Transactional 注解,将注解里的属性设置到 TransactionAttribute 的实现类属性里。除了这个,另还有两个实现,分别是 JTA 事务注解解析器,和 EJB 事务注解管理解析器,区别是解析的注解不同,spring 是 @Transactional,jta 是 javax.transaction.Transactional,EJB 是 javax.ejb.TransactionAttribute。这个地方应用和 apache dubbo2.7.x 版本解析 dubbo 的 @service 注解是一样一样的。关键代码如下,通过 AnnotatedElementUtils 类,这个类在 spring-core 包下,找到注解属性集 AnnotationAttributes,如果不为空,则包装成事务属性集返回

    	@Override
    	@Nullable
    	public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    		AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
    				element, Transactional.class, false, false);
    		if (attributes != null) {
    			return parseTransactionAnnotation(attributes);
    		}
    		else {
    			return null;
    		}
    	}
    
    

    AnnotationTransactionAttributeSource

    见名知意,这个类是注解事务属性集的源,怎么理解呢?spring 抽象了获取事务属性集的行为,而 AnnotationTransactionAttributeSource 正是 @Transactional 注解方式的事务属性集收集实现。SpringTransactionAnnotationParser 就是作用于这个里面,用于发现 @Transactiona 注解的方法

    TransactionAttributeSourcePointcut

    也是见名知意,Pointcut 属于 aop 的概念范畴,需要了解 spring aop 的知识才能看明白,这个就是 @Transactional 注解的切点,AnnotationTransactionAttributeSource 作用于此,用于寻找 @Transactiona 注解的方法,关键代码如下:

    	public boolean matches(Method method, Class targetClass) {
    		TransactionAttributeSource tas = getTransactionAttributeSource();
    		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    	}
    
    

    TransactionInterceptor

    事务的拦截器。aop 编程里,有了切入点 Pointcut,就要有通知 advice,我们熟悉的 spring aop 里有前置、后置、环绕、异常等通知类型,TransactionInterceptor 属于自定义通知模型实现,实现自 Advice 接口,类似于环绕通知,具体见类结构图,如下:

    核心方法如下:

    	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, invocation::proceed);
    	}
    
    

    被 @Transactional 注解的方法,如果被 aop 正确的增强了,运行的时候都会进入到这个方法里面,如果你发现事务不生效啊等等问题,可以从这里开始定位真实原因

    BeanFactoryTransactionAttributeSourceAdvisor

    事务增强器,用于增强添加了 @Transactional 注解的方法,上面提到的这些核心类,最终都作用于这里,用于寻找 @Transactional 注解的方法和织入事务处理逻辑

    ProxyTransactionManagementConfiguration

    代理事务管理的配置类,上面介绍的这些事务 aop 编程相关的在这个里面组合配置生效的,同时,如果你有特殊的个性化的需求,也可以自定义注册这个里面的实例。比如我嫌弃 @Transactional 注解太长了,想用 @Tx 注解。没关系,直接定义个 TransactionAttributeSource 实现,解析 @Tx 的方法,然后注册到 spring 的上线文中即可。代码如:

    @Configuration(proxyBeanMethods = false)
    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.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;
    	}
    
    }
    
    

    事务抽象核心类释义

    PlatformTransactionManager

    平台事务管理器,这是 Spring 事务基础设施中的中心接口。它定义了三个最最基本的事务方法,getTransaction 获取事务,包含了事务开启的行为,commit 提交事务,rollback 回滚事务。代码如下:

    public interface PlatformTransactionManager extends TransactionManager {
    	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
    			throws TransactionException;
    	void commit(TransactionStatus status) throws TransactionException;
    	void rollback(TransactionStatus status) throws TransactionException;
    }
    
    

    在 spring-tx 中并没有提供真正的实现类,只提供了一个抽象派生类 AbstractPlatformTransactionManager,并建议其他实现基于这个派生类,因为它预先实现了定义的传播行为并处理事务同步处理。子类必须为底层事务的特定状态实现模板方法,例如: begin、suspend、resume、commit 等。我们平时常见的实现有:JpaTransactionManager、JtaTransactionManager、DataSourceTransactionManager 等。事务管理器和事务 aop 处理的逻辑本身没有任何耦合,只需将 PlatformTransactionManager 实例注册到 spring 上下文中即可,事务拦截器会通过获取到 @Transactional 里的 transactionManager 属性去上下文中寻找事务管理器,并将其缓存起来,见 TransactionAspectSupport.java 里的 determineTransactionManager 方法

    	protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
    		// Do not attempt to lookup tx manager if no tx attributes are set
    		if (txAttr == null || this.beanFactory == null) {
    			return asPlatformTransactionManager(getTransactionManager());
    		}
    
    		String qualifier = txAttr.getQualifier();
    		if (StringUtils.hasText(qualifier)) {
    			return determineQualifiedTransactionManager(this.beanFactory, qualifier);
    		}
    		else if (StringUtils.hasText(this.transactionManagerBeanName)) {
    			return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
    		}
    		else {
    			PlatformTransactionManager defaultTransactionManager = asPlatformTransactionManager(getTransactionManager());
    			if (defaultTransactionManager == null) {
    				defaultTransactionManager = asPlatformTransactionManager(
    						this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY));
    				if (defaultTransactionManager == null) {
    					defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
    					this.transactionManagerCache.putIfAbsent(
    							DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
    				}
    			}
    			return defaultTransactionManager;
    		}
    	}
    
    

    TransactionStatus

    事务状态抽象,用这个类的实现来维护当前的事务状态,spring-tx 里提供了默认的实现 DefaultTransactionStatus。一般情况下这个不需要我们关心,它和 PlatformTransactionManager 是成对存在的,心细的你可能已经发现了,PlatformTransactionManager 里的三个事务行为传递的就是 TransactionStatus。我们知道事务 aop 增强了添加 @Transactional 的方法,在执行方法前调用 PlatformTransactionManager.getTransaction 开启事务, 之后调用 commit 方法提交事务,提交事务的入参 TransactionStatus 就是开启事务获得的。参见 TransactionAspectSupport.java 里的 createTransactionIfNecessary 方法

    TransactionDefinition

    事务定义,对应了 TransactionAttribute,最终通过 aop 得到的 TransactionAttribute 里的属性会被传递到 TransactionDefinition 里,所以 TransactionDefinition 里也包含了所有事务相关的属性,PlatformTransactionManager.getTransaction 正是通过这个里面的属性去获取的事务。AbstractPlatformTransactionManager 派生类里也是通过这个里面的属性去判断协调 spring 的事务传播行为的

    结语

    当梳理完 spring-tx 模块的整个结构和工作方式后,仿佛拉开了 spring 事务管理的面纱,很多事务的执行细节一览无余。很多事务相关的问题也就很容易解释了。比如常见的类中的方法直接调用方法事务不生效等问题,以及可以非常清晰的理解 spring 的传播行为的真正含义等。最后预告下,spring 对于多数据源的事务处理解决方案 ChainedTransactionManager

  • 相关阅读:
    协程
    多进程
    多线程
    模块进阶
    内建函数
    内建属性
    属性property
    私有化
    深拷贝、浅拷贝
    ==、is
  • 原文地址:https://www.cnblogs.com/xmanblue/p/15246882.html
Copyright © 2011-2022 走看看