返回

Spring5——事务传播机制原理解析

发布时间:2023-01-10 23:11:55 269
# html# java# spring# java# 数据

前言

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务上下文的规则(是否要有独立的事务隔离级别和锁)。

概述

当我们调用一个基于Spring的Service接口的事务方法(如UserService#addUser())时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其它的Service接口方法以共同完成一个完整的业务操作,因此就会产生服务接口方法嵌套调用的情况, Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。

 

事务传播是Spring进行事务管理的重要概念,其重要性怎么强调都不为过。但是事务传播行为也是被误解最多的地方,在本文里,我们将详细分析不同事务传播行为的表现形式,掌握它们之间的区别。

事务传播行为种类

  • PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。 配置方式:TransactionDefinition.PROPAGATION_REQUIRED

  • PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。 配置方式:TransactionDefinition.PROPAGATION_SUPPORTS

  • PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。 配置方式:TransactionDefinition.PROPAGATION_MANDATORY

  • PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。 配置方式:TransactionDefinition.PROPAGATION_REQUIRES_NEW

  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 配置方式:TransactionDefinition.PROPAGATION_NOT_SUPPORTED

  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 配置方式:TransactionDefinition.PROPAGATION_NEVER

  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 配置方式:TransactionDefinition.PROPAGATION_NESTED

首先我们要明确几点基本知识:

  • 1、事务传播机制只适用于不同bean之间方法的调用,如果一个bean中的两个方法互相调用并不会使用到事务传播。比如,一个bean的method1的事务传播级别为Required,method2的事务传播级别为Never,我们在method1里面调用method2。首先method1会开启一个事务,而method2也没有报错并正确执行了,而且也在method1开启的事务之中,说明事务传播机制在一个bean自己的方法互相调用中并不起作用,只要一个方法开启了事务,那这个在方法里调用当前bean的其他方法都在这个事务中运行,而不管其他方法的事务传播机制是如何配置的。

  • 2、事务方法里如果抛RuntimeException,则会导致所有相关事务回滚,个别事务传播机制有点特殊,我们下面会讲到。

  • 3、事务方法里如果抛Throwable或者Exception,默认不会导致相关事务回滚,一般都会在出异常的地方提交,就有可能出现部分提交的问题。但可以配置rollback-for属性来控制。

源码解析:AbstractPlatformTransactionManager

  • 事务的传播特性源码主要位于AbstractPlatformTransactionManager类的handleExistingTransaction方法中。

PROPAGATION_NEVER

  • 该特性规定了以非事物方式执行,如果当前存在事物,则抛出异常。
private TransactionStatus handleExistingTransaction(
		TransactionDefinition definition, Object transaction, boolean debugEnabled)
		throws TransactionException {

	// 1.PROPAGATION_NEVER --> 以非事物方式执行,如果当前存在事物,则抛出异常。
	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
		throw new IllegalTransactionStateException(
				"Existing transaction found for transaction marked with propagation 'never'");
	}

	......
}

PROPAGATION_NOT_SUPPORTED

  • 该特性规定了以非事物方式执行,如果当前存在事物,则挂起当前事物。
private TransactionStatus handleExistingTransaction(
		TransactionDefinition definition, Object transaction, boolean debugEnabled)
		throws TransactionException {

	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
		if (debugEnabled) {
			logger.debug("Suspending current transaction");
		}
		// 重点:挂起已有事物
		Object suspendedResources = suspend(transaction);
		boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
		// 创建新事物,注意:transaction参数为null,所以这里创建的不是一个真正的事物
		return prepareTransactionStatus(
				definition, null, false, newSynchronization, debugEnabled, suspendedResources);
	}

	......
}

这里又涉及到一个概念,事务挂起(这个前面没有介绍,因为前面都是单个事物)。下面分析一下事物挂起的流程:

@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
	// 1.如果存在事物同步回调接口
	if (TransactionSynchronizationManager.isSynchronizationActive()) {
		// 1.1 挂起事务同步回调接口
		List suspendedSynchronizations = doSuspendSynchronization();
		try {
			// 挂起事物
			Object suspendedResources = null;
			if (transaction != null) {
				suspendedResources = doSuspend(transaction);
			}
			// 获取已有事物名称
			String name = TransactionSynchronizationManager.getCurrentTransactionName();
			// 清空已有事物名称
			TransactionSynchronizationManager.setCurrentTransactionName(null);
			// 获取已有事物的readOnly属性值
			boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
			// 将已有事物的readOnly属性值设置为false
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
			// 获取已有事物数据库事物隔离级别
			Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
			// 清空已有事物数据库事物隔离级别
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
			// 获取已有事物激活标识
			boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
			// 将当前事物激活标识设置为false
			TransactionSynchronizationManager.setActualTransactionActive(false);
			//将上面获取到的一系列事物属性,重新封装至SuspendedResourcesHolder对象,并返回
			return new SuspendedResourcesHolder(
					suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
		}
		catch (RuntimeException | Error ex) {
			// doSuspend failed - original transaction is still active...
			doResumeSynchronization(suspendedSynchronizations);
			throw ex;
		}
	}
	// 不存在事物同步回调接口,且当前事物不为空
	else if (transaction != null) {
		// Transaction active but no synchronization active.
		// 事物已经被激活,但是没有事物同步回调,则直接挂起当前事物即可
		Object suspendedResources = doSuspend(transaction);
		// 返回挂起的事物资源
		return new SuspendedResourcesHolder(suspendedResources);
	}
	// 处理没有事物的情况...
	else {
		// Neither transaction nor synchronization active.
		return null;
	}
}

首先来看事物同步回调接口,该接口会在prepareSynchronization方法中设置,即使我们没有自定义事物同步回调接口,Spring默认也会为当前事物创建一个空的事物同步回调接口。关于该接口的使用,可以参考上一节中的例子。(注意:事物同步回调接口是与当前线程绑定的)。下面看事物同步接口是如何挂起的:

private List doSuspendSynchronization() {
	// 1.获取当前线程的所有事物同步回调
	List suspendedSynchronizations =
			TransactionSynchronizationManager.getSynchronizations();
	// 2.循环并挂起所有同步回调接口
	for (TransactionSynchronization synchronization : suspendedSynchronizations) {
		synchronization.suspend();
	}
	// 3.清除资源
	TransactionSynchronizationManager.clearSynchronization();
	return suspendedSynchronizations;
}

其次来看挂起事物的流程,注释里已经写了很清楚了,最后会将已有的事物属性封装到SuspendedResourcesHolder对象中,该类就持有了被挂起事物的属性。接下来调用prepareTransactionStatus方法并将suspendedResources入参,这样一来,新创建的事物就持有了被挂起事物的的属性,就会形成一个事物链。而且新创建的事物transaction参数为null,所以PROPAGATION_NOT_SUPPORTED特性是不会真正开启事物的。

// 创建新事物,注意:transaction参数为null,所以这里创建的不是一个真正的事物
return prepareTransactionStatus(
		definition, null, false, newSynchronization, debugEnabled, suspendedResources);

PROPAGATION_REQUIRES_NEW

  • 该特性下会挂起已有事物并新建一个事物。
private TransactionStatus handleExistingTransaction(
		TransactionDefinition definition, Object transaction, boolean debugEnabled)
		throws TransactionException {

	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
		if (debugEnabled) {
			logger.debug("Suspending current transaction, creating new transaction with name [" +
					definition.getName() + "]");
		}
		// 挂起已有事物
		SuspendedResourcesHolder suspendedResources = suspend(transaction);
		try {
			return startTransaction(definition, transaction, debugEnabled, suspendedResources);
		}
		catch (RuntimeException | Error beginEx) {
			resumeAfterBeginException(transaction, suspendedResources, beginEx);
			throw beginEx;
		}
	}

	......
}


private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
		boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

	boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
	// 创建事物
	DefaultTransactionStatus status = newTransactionStatus(
			definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
	// 开启事物
	doBegin(transaction, definition);
	// 初始化事物同步属性
	prepareSynchronization(status, definition);
	return status;
}

PROPAGATION_NESTED

private TransactionStatus handleExistingTransaction(
		TransactionDefinition definition, Object transaction, boolean debugEnabled)
		throws TransactionException {

	if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
		// 如果不允许嵌套事物,则抛出异常
		if (!isNestedTransactionAllowed()) {
			throw new NestedTransactionNotSupportedException(
					"Transaction manager does not allow nested transactions by default - " +
					"specify 'nestedTransactionAllowed' property with value 'true'");
		}
		if (debugEnabled) {
			logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
		}
		//下面对JtaTransactionManager和AbstractPlatformTransactionManager分别进行处理
		//useSavepointForNestedTransaction(),是否为嵌套事务使用保存点
		// 1.对于JtaTransactionManager-->返回false
		// 2.对于AbstractPlatformTransactionManager-->返回true
		if (useSavepointForNestedTransaction()) {
			// Create savepoint within existing Spring-managed transaction,
			// through the SavepointManager API implemented by TransactionStatus.
			// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
			// 创建保存点在现有spring管理事务,通过TransactionStatus SavepointManager API实现。
			// 通常使用JDBC 3.0保存点。永远不要激活Spring同步。
			DefaultTransactionStatus status =
					prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
			// 创建保存点
			status.createAndHoldSavepoint();
			return status;
		}
		else {
			// Nested transaction through nested begin and commit/rollback calls.
			// Usually only for JTA: Spring synchronization might get activated here
			// in case of a pre-existing JTA transaction.
			// 通过嵌套的开始,提交调用,及回滚调用进行嵌套事务。
			// 只对JTA有效,如果已经存在JTA事务,这里可能会激活Spring同步。
			return startTransaction(definition, transaction, debugEnabled, null);
		}
	}

	......
}


private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
		boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

	boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
	// 创建事物
	DefaultTransactionStatus status = newTransactionStatus(
			definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
	// 开启事物
	doBegin(transaction, definition);
	// 初始化事物同步属性
	prepareSynchronization(status, definition);
	return status;
}

这里对AbstractPlatformTransactionManager和JtaTransactionManager分别做了不同的处理,重点看前者。AbstractPlatformTransactionManager是允许使用保存点的。接下来看保存点的创建过程:

public abstract class AbstractTransactionStatus implements TransactionStatus {

	// 创建保存点
	public void createAndHoldSavepoint() throws TransactionException {
		setSavepoint(getSavepointManager().createSavepoint());
	}
}


public class DefaultTransactionStatus extends AbstractTransactionStatus {

	// 获取SavepointManager
	@Override
	protected SavepointManager getSavepointManager() {
		Object transaction = this.transaction;
		if (!(transaction instanceof SavepointManager)) {
			throw new NestedTransactionNotSupportedException(
					"Transaction object [" + this.transaction + "] does not support savepoints");
		}
		// SavepointManager就是当前事物
		return (SavepointManager) transaction;
	}
}


public abstract class JdbcTransactionObjectSupport implements SavepointManager, SmartTransactionObject {

	// 创建保存点
	@Override
	public Object createSavepoint() throws TransactionException {
		// 获取ConnectionHolder
		ConnectionHolder conHolder = getConnectionHolderForSavepoint();
		try {
			// 如果当前连接不支持保存点,抛出异常
			if (!conHolder.supportsSavepoints()) {
				throw new NestedTransactionNotSupportedException(
						"Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
			}
			// 如果当前连接的RollbackOnly已经被标记为true,抛出异常
			if (conHolder.isRollbackOnly()) {
				throw new CannotCreateTransactionException(
						"Cannot create savepoint for transaction which is already marked as rollback-only");
			}
			// 创建保存点
			return conHolder.createSavepoint();
		}
		catch (SQLException ex) {
			throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);
		}
	}
}

总体来讲创建保存点的过程比较简单,最终会调用数据库驱动的底层方法去创建保存点,感兴趣的可以自己跟踪查看。创建完保存点之后将结果设置到TransactionStatus对象中。 该特性下会将新事物在原有事物内,以嵌套的方式运行,该特性也不会开启一个新的事物。

PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED

private TransactionStatus handleExistingTransaction(
		TransactionDefinition definition, Object transaction, boolean debugEnabled)
		throws TransactionException {

	// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
	if (debugEnabled) {
		logger.debug("Participating in existing transaction");
	}
	if (isValidateExistingTransaction()) {
		// 验证事物隔离级别
		// 如果当前事物的隔离级别不为默认隔离级别,则比较当前事物隔离级别与已有事物隔离级别,
		// 如不同,则抛出事物隔离级别不兼容异常
		if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
			Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
			if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
				Constants isoConstants = DefaultTransactionDefinition.constants;
				throw new IllegalTransactionStateException("Participating transaction with definition [" +
						definition + "] specifies isolation level which is incompatible with existing transaction: " +
						(currentIsolationLevel != null ?
								isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
								"(unknown)"));
			}
		}
		
		// 验证事物只读属性
		// 如果当前事物可写,但是已有的事物是只读,则抛出异常
		if (!definition.isReadOnly()) {
			if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
				throw new IllegalTransactionStateException("Participating transaction with definition [" +
						definition + "] is not marked as read-only but existing transaction is");
			}
		}
	}
	boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
	return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}

isValidateExistingTransaction()该函数的作用是指定新事物参与已有事物时,新旧两个事物的验证级别。该属性值默认为false,宽松范围的验证,也就是不验证。如果将该属性值改为true的话,那么将会验证新旧两个事物的数据库事物隔离级别、事物只读属性是否相同。对于PROPAGATION_MANDATORY,从Spring的注释上没有看到对改属性的处理,但是该属性也会走这个分支,即使用已有事物,由于到这里肯定是已经存在事物的,该特性在这里不会抛出异常。 该特性也不会开启一个新的事物,而是继续在原有事物中运行或者以非事物方式运行

 

参考: https://www.cnblogs.com/softidea/p/5962612.html

特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线