@Transaction源码解读

@Transaction注解相信读者们肯定都用过,spring通过这种注解式声明事务,使我们开发者无需关注与数据库的事务编写,更多的关注到业务逻辑的代码编写上,使编码更加简单,但是@Transaction是如何工作的,以及面试中常问到的事务的传播行为是怎么一回事,我们还是有必要了解一下,本文将带你从源码层面上理解这这些东西,如有错误欢迎各位指正并讨论。

当我们需要使用@Transaction注解声明事务时,必须先要使用@EnableTransactionManagement开启事务注解的支持,现在大多数都在用springboot框架进行编码,我们几乎没有写过@EnableTransactionManagement,这是因为springboot中采用自动装配TransactionAutoConfiguration这个自动配置类已经将@EnableTransactionManagement注解写入该类了,所以无需我们编写。那我们看一下这个类干了些什么:

一般spring的尿性都是都是通过@Enablexxx注解导入一个Import相关的类.,这个也不列外

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

     
    //是否要创建基于子类的目标代理
    boolean proxyTargetClass() default false;


   //使用那种模式进行代理
	AdviceMode mode() default AdviceMode.PROXY;
    
   
    //所有切面的排序
	int order() default Ordered.LOWEST_PRECEDENCE;

}

TransactionManagementConfigurationSelector这个类我们主要是看它父类#AdviceModeImportSelector.selectImports()方法

//importingClassMetadata:代表bean的注解信息
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
	
		Class annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
		Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
		//获取注解的属性值
		AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
		if (attributes == null) {
			throw new IllegalArgumentException(String.format(
					"@%s is not present on importing class '%s' as expected",
					annType.getSimpleName(), importingClassMetadata.getClassName()));
		}
        //默认情况下模式未PROXY
		AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
        //调用子类的selectImports()重载方法
		String[] imports = selectImports(adviceMode);
		if (imports == null) {
			throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
		}
		return imports;
	}

接下来我们看子类的#TransactionManagementConfigurationSelector.selectImports()方法

	@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;
		}
	}

因为默认是PROXY模式,这里的意思就是往spring容器中注入一个

ProxyTransactionManagementConfiguration类型的bean

接下来我们看ProxyTransactionManagementConfiguration这个bean干了些什么事

//这个bean不开启CGLIB代理
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class
ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

	/**
	 * 定义事务切面
	 * @param transactionAttributeSource
	 * @param transactionInterceptor
	 * @return
	 */
	@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);
		//enableTx 设置排序 也是从导入类的AnnotationMetada中获取
		if (this.enableTx != null) {
			advisor.setOrder(this.enableTx.getNumber("order"));
		}
		return advisor;
	}


	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionAttributeSource transactionAttributeSource() {
		return new AnnotationTransactionAttributeSource();
	}

	/**
	 * 设置事务切面的MethodInterceprtor
	 * @param transactionAttributeSource
	 * @return
	 */
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
		TransactionInterceptor interceptor = new TransactionInterceptor();
		/**
		 * 注入transactionAttributeSource进行方法的match
		 */
		interceptor.setTransactionAttributeSource(transactionAttributeSource);
		if (this.txManager != null) {
			interceptor.setTransactionManager(this.txManager);
		}
		return interceptor;
	}

}

可以看到这个配置bean又往容器注入了3个类型的bean

首先来分析第一个bean,这里就会涉及到一些spring-aop相关的知识了

1. BeanFactoryTransactionAttributeSourceAdvisor 

面向切面编程中有一个核心的概念就是切面,spring中的advisor就可以理解为一个切面,一个切面又主要由切入点pointCut和增强器advice组成。

 事务这个advisor的切入点point就是TransactionAttributeSourcePointcut,增强器advisor就是TransactionInterceptor

TransactionAttributeSourcePointcut主要做两件事

第一件事是做类匹配,判断这个这个类是否需要进行代理主要是通过TransactionAttributeSourceClassFilter的matches方法 

第二件事就是方法匹配,若方法上需要进行代理那么这个类肯定进行代理比如方法上加了@Transaction注解,方法匹配的逻辑就是TransactionAttributeSourcePointcut的matches方法

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	/**
	 * 类匹配基本不做什么,可以过滤掉
	 */
	protected TransactionAttributeSourcePointcut() {
		setClassFilter(new TransactionAttributeSourceClassFilter());
	}


	/**
	 * 方法匹配
	 * @param method
	 * @param targetClass
	 * @return
	 */
	@Override
	public boolean matches(Method method, Class targetClass) {
		//获取注入的事务属性解析器
		TransactionAttributeSource tas = getTransactionAttributeSource();
		//判断方法或者类上是否有@Transaction注解,并将@Transaction的属性值封装成TransactionAttribute放入缓存中
		return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
	}

主要就是看tas.getTransactionAttribute(method, targetClass)方法调用,这个方法主要就是收集@Transaction注解的一些属性比如事务的隔离级别 传播行为,收集后在放入缓存中

从这里 可以看到几个关于@Transaction注解不生效的原因

1.若方法所属的类为Object类型,不进行处理

if (method.getDeclaringClass() == Object.class) {
   return null;
}

2.被修饰的方法只能是一个pubic类型的

if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
   return null;
}

截至到这里 做的主要就是判断这个类是否需要进行事务相关的代理,并收集@Transaction注解相关的属性放入缓存中,若需要则把对象升级成代理对象放入容器中,待到真正调用方法时,根据aop的调用逻辑会调到TransactionInterceptor这个类的invoke()方法,那么我们接下来就来仔细研究一下#TransactionInterceptor.invoke()方法

@Override
	@Nullable
	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对象
		Class targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

由于invokeWithinTransaction()的方法逻辑比较长 我们分成几段来看

1.首先看第一段逻辑

	//获取事务属性管理器,里面有TransactionAttribute的缓存
		TransactionAttributeSource tas = getTransactionAttributeSource();
		//获取缓存中的TransactionAttribute
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

这里主要就是获取@Transaction注解的属性信息,信息在前面执行match的时候就被封装成了一个TransactionAttribute对象

2.获取事务管理器

final TransactionManager tm = determineTransactionManager(txAttr);




protected TransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
		// Do not attempt to lookup tx manager if no tx attributes are set
		if (txAttr == null || this.beanFactory == null) {
			return 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 {
			TransactionManager defaultTransactionManager = getTransactionManager();
			if (defaultTransactionManager == null) {
				defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
				if (defaultTransactionManager == null) {
					//直接从容器中获取
					defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);
					//获取到后放入缓存
					this.transactionManagerCache.putIfAbsent(
							DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
				}
			}
			return defaultTransactionManager;
		}
	}

这个事务管理器99%的情况都是需要我们手动注入到容器中才能获取到,一般情况下会往容器中注入一个DataSourceTransactionManager类型的bean

3.获取连接点的名字,其实就是方法名

final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

4.创建事务

	
			 /* 
			 * ptm:事务管理器 管理了数据源
			 * txAttr:事务属性 @Transaction注解的属性
			 * joinpointIdentification 连接点 方法名
			 */
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

那么我们就仔细看一下这个事务是如何创建的

4.1首先根据事务管理器创建status

  获取事务

Object transaction = doGetTransaction();


protected Object doGetTransaction() {
	DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    //允许设置回滚点和事务传播行为有关
	txObject.setSavepointAllowed(isNestedTransactionAllowed());
	ConnectionHolder conHolder =
		(ConnectionHolder)TransactionSynchronizationManager.getResource(obtainDataSource());
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

这里创建了一个DataSourceTransactionObject对象,

TransactionSynchronizationManager.getResource(obtainDataSource())这行代码致关重要,主要就是threadLocal中存储了一个map,map的key就是datasource数据源,vaule就是Connection连接对象,这里将它封装成spring的ConnectionHolder对象,这里第一次进来都没有设置肯定是空的。

这里的if判断主要就是ConnectionHolder是否为空,所以不会进,里面的逻辑就是处理事务的传播行为的

if (isExistingTransaction(transaction)) {
   // Existing transaction found -> check propagation behavior to find out how to behave.
   return handleExistingTransaction(def, transaction, debugEnabled);
}
紧接着看着行代码
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}

TransactionDefinition.PROPAGATION_MANDATORY属于传播行为的一种,就是如果当前线程不存在事务,就直接报错,表示强制要使用别人创建好的事务 不需要我自己创建

当执行到这里时,这里就是我们需要关注的传播行为

else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			//挂起当前事务,这里一般为null
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}

当传播行为属于PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,

PROPAGATION_NESTED其中一种时就会创建一个新的事务,新的事务其实就是从连接池中获取一个连接对象,并关闭自动提交,那我们就来看一下startTransaction()方法,记住这个方法,不管那种传播行为,只要调用这个方法就一定会创建一个新的事务。

看doBegin(transaction, definition);这个方法

首先从连接池中获取一个连接对象,并填充DataSourceTransactionObject中connectionHolder的值,因为之前从线程上下文获取为空,这里不为空,这里还有一个属性为true,表示它是一个新的connectionHolder对象

Connection newCon = obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);

后面就是设置连接的属性 比如隔离级别,是否只读等,最后还要关闭自动提交con.setAutoCommit(false);

当这些都设置好后,将事务放入线程上下文中此时具有事务功能的连接对象就创建好了

	if (txObject.isNewConnectionHolder()) {
				//如果是一个新的ConnectionHolder,将ConnectionHolder放入ThradLocal中
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}

开启事务后 ,还要开启一些spring的功能

	protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
		//判断是否为新的newSynchronization
		if (status.isNewSynchronization()) {
			//设置是否有事务,即DataSourceTransactionObject不为null
			TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
			//设置事务的隔离级别
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
					definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
							definition.getIsolationLevel() : null);
			//设置当前事务的只读属性
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
			//设置当前事务的名字,即TransactionAttribute的name  方法名
			TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
			//初始换synchronizations,可在synchronizations容器中注册TransactionSynchronization用于一些业务处理如afterCommit,beforeCommit
			TransactionSynchronizationManager.initSynchronization();
		}
	}

事务开启完毕后,继续执行aop的调用链 invocation.proceedWithInvocation();最终执行业务逻辑

在catch里面就是执行回滚逻辑

completeTransactionAfterThrowing(txInfo, ex);

若不报错 则执行事务的提交

commitTransactionAfterReturning(txInfo);

再看回滚和提交逻辑前我们先看一下事务的传播行为是如何进行的

我们在进入到这个方法

if (isExistingTransaction(transaction)) {
   // Existing transaction found -> check propagation behavior to find out how to behave.
   /**
    * 如果当前线程存在事务 即从ThradLocal中能获取到ConnectionHolder
    * 且ConnectionHolder为活跃状态
    * def:TransactionAttribute 事务属性
    * transaction:DataSourceTransactionObject  数据源事务对象
    */
   return handleExistingTransaction(def, transaction, debugEnabled);
}

若当前存在事务则进行handleExistingTransaction()逻辑

 //PROPAGATION_NEVER  当前环境有事务 直接报错
	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
			throw new IllegalTransactionStateException(
					"Existing transaction found for transaction marked with propagation 'never'");
		}
        // PROPAGATION_NOT_SUPPORTED 若当前环境有事务 将事务挂起
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
			if (debugEnabled) {
				logger.debug("Suspending current transaction");
			}
			//挂起
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}

我们来看我们比较熟悉的当事务的传播行为PROPAGATION_REQUIRES_NEW时,事务是如何进行操作的

首先将原来的事务挂起

SuspendedResourcesHolder suspendedResources = suspend(transaction);主要的就是将原来的connectionHolder对象从线程上下文中移除,并将当前事务封装成一个SuspendedResourcesHolder对象,

接着创建一个新的事务,我们之前说过,碰到startTransaction()就是创建一个新的事务startTransaction(definition, transaction, debugEnabled, suspendedResources);    
这个方法就不细说了,这里就是多了suspendedResources关于旧事务的封装,把它和当前事务绑定了,主要就是用于当时事务提交后,恢复旧事务用的。

因为REQUIRED_NEW会创建一个新的连接所以这里有可能会发生死锁,关于这块可以看另一篇文章

@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)也会导致死锁吗?_1996_ZK的博客-CSDN博客线上的服务,突然就卡死了,整个服务不可用了,必须要重启才能解决,但重启过后,过一段时间就又出现了,后来通过jstack命令排查到是获取数据库连接对象时,tomcat的线程阻塞在那里导致线程被耗尽(Connection newCon = obtainDataSource().getConnection();),最终造成服务不可用。但究竟是什么原因造成获取连接一直阻塞呢?后来通过压测发现只要并发数超过了连接池的最大连接数,这个问题就必现,下面的代码是模拟生产的代码写的demo操作表A...https://blog.csdn.net/weixin_43716742/article/details/124433561接着我们看另外一种传播行为PROPAGATION_NESTED,这个和PROPAGATION_REQUIRES_NEW不同 ,不会创建一个新的事务只是设置一个回滚点。

回滚点的意思就是只会回滚到创建回滚点这之间的业务逻辑,之前的并不会回滚,具体可以看jdbc的相关说明。这里就做了一件事,设置回滚点

getConnection().setSavepoint(SAVEPOINT_NAME_PREFIX + this.savepointCounter);

如果播行为PROPAGATION_REQUIRED,那么就会执行

prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

接着我们回过头来看回滚逻辑

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;

			try {
				triggerBeforeCompletion(status);
                 //若有回滚点
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Rolling back transaction to savepoint");
					}
					//回滚到回滚点
					status.rollbackToHeldSavepoint();
				}
				//是一个新事务
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction rollback");
					}
					//回滚
					doRollback(status);
				}
				else {
					// Participating in larger transaction
					if (status.hasTransaction()) {
						if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
							}
							/**
							 * 若检测到异常则设置  ConnectionHolder的rollbackonly属性为true
							 */
							doSetRollbackOnly(status);
						}
						else {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
							}
						}
					}
					else {
						logger.debug("Should roll back transaction but cannot - no transaction available");
					}
					// Unexpected rollback only matters here if we're asked to fail early
					if (!isFailEarlyOnGlobalRollbackOnly()) {
						unexpectedRollback = false;
					}
				}
			}
			catch (RuntimeException | Error ex) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw ex;
			}
            //执行afterCompletion操作
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

			// Raise UnexpectedRollbackException if we had a global rollback-only marker
			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
		}
		finally {
			//清空ThreadLocal中的属性
			cleanupAfterCompletion(status);
		}
	}

这里就是执行正常的回滚逻辑,只是这里需要注意一个属性,当传播行为PROPAGATION_REQUIRED时会设置doSetRollbackOnly(status);这个属性和提交的时候有关,在提交的时候有这段逻辑判断

      	if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
			if (defStatus.isDebug()) {
				logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
			}
			processRollback(defStatus, true);
			return;
		}



if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}

如果这个值被设置成了true即使外部事务未捕获到异常,事务也不会正常提交。

后面还会写一篇spring整合mybatis时事务是如何执行的

你可能感兴趣的:(spring,java,spring,spring,boot)