Spring源码分析十六:事务实现③ - 事务的创建

一、前言

本文是笔者阅读Spring源码的记录文章,由于本人技术水平有限,在文章中难免出现错误,如有发现,感谢各位指正。在阅读过程中也创建了一些衍生文章,衍生文章的意义是因为自己在看源码的过程中,部分知识点并不了解或者对某些知识点产生了兴趣,所以为了更好的阅读源码,所以开设了衍生篇的文章来更好的对这些知识点进行进一步的学习。

全集目录:Spring源码分析:全集整理


由于 事务的源码和 前篇的 Aop 源码逻辑很类似,所以本篇中某些内容不会展开去讲解,建议先阅读完 Spring源码分析十一:@AspectJ方式的AOP再来阅读本文会更好理解。


这是一个巨长的篇章…
全集目录如下:

  1. Spring源码分析十四:事务实现① - AutoProxyRegistrar
  2. Spring源码分析十五:事务实现② - ProxyTransactionManagementConfiguration
  3. Spring源码分析十六:事务实现③ - 事务的创建
  4. Spring源码分析十七:事务实现④ - 事务的回滚
  5. Spring源码分析十八:事务实现⑤ - 事务的提交

1. TransactionSynchronizationManager

TransactionSynchronizationManagerTransactionSynchronizationManager 中使用 ThreadLocal 保存了在不同线程中不同事务的信息。

public abstract class TransactionSynchronizationManager {
    
    

	private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);

	private static final ThreadLocal<Map<Object, Object>> resources =
			new NamedThreadLocal<>("Transactional resources");

	private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
			new NamedThreadLocal<>("Transaction synchronizations");

	private static final ThreadLocal<String> currentTransactionName =
			new NamedThreadLocal<>("Current transaction name");

	private static final ThreadLocal<Boolean> currentTransactionReadOnly =
			new NamedThreadLocal<>("Current transaction read-only status");

	private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
			new NamedThreadLocal<>("Current transaction isolation level");

	private static final ThreadLocal<Boolean> actualTransactionActive =
			new NamedThreadLocal<>("Actual transaction active");
	...
}

我们从上面的部分代码可以看到,TransactionSynchronizationManager 中保存的是各个线程中的事务信息。

2. 事务传播属性

如果有不了解 事务传播属性的,也可以先移步观看 事务特性原理及其原理、隔离级别和传播属性,否则后面的判断可能不好理解。

事务传播属性 解释
PROPAGATION_REQUIRED 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。即如果上级具有事务,则使用上级的事务,不具备则自己新建一个事务
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。即如果上级存在事务,则挂起上级事务,使用自己新创建的事务
PROPAGATION_MANDATORY 支持当前事务,如果当前没有事务,就抛出异常。即如果上级具有事务,则使用上级的事务,上级没有事务,则抛出异常
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。即如果上级具有事务,则使用上级的事务,如果上级没有事务,则不开启事务
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。即如果上级具有事务,则使用挂起上级事务,使用非事务方式。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

这里解释一下 PROPAGATION_NESTEDPROPAGATION_REQUIRES_NEW 的区别:

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全
commitedrolledback 而不依赖于外部事务,它拥有自己的隔离范围, 自己的锁, 等等.
当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
PROPAGATION_REQUIRES_NEW常用于日志记录,或者交易失败仍需要留痕

另一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正
的子事务. 潜套事务开始执行时, 它将取得一个 savepoint.
如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分,
只有外部事务结束后它才会被提交.

由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于:

PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.

二、事务的创建 - createTransactionIfNecessary

createTransactionIfNecessary 的实现是在TransactionAspectSupport#createTransactionIfNecessary 中,完成了事务的创建,这里面考虑了事务的传播属性的处理,所以并不是一定会创建事务,根据传播属性的不同会有不同的处理。

详细代码如下:

	// TransactionAttribute 是解析出来的事务
	protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    
    

		// If no name specified, apply method identification as transaction name.
		// 如果没有名称指定则使用方法唯一标识,并使用  DelegatingTransactionAttribute 封装 txAttr
		if (txAttr != null && txAttr.getName() == null) {
    
    
			txAttr = new DelegatingTransactionAttribute(txAttr) {
    
    
				@Override
				public String getName() {
    
    
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		if (txAttr != null) {
    
    
			if (tm != null) {
    
    
				// 获取事务
				status = tm.getTransaction(txAttr);
			}
			else {
    
    
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
							"] because no transaction manager has been configured");
				}
			}
		}
		// 构建事务信息
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

这里我们可以看到其基本逻辑如下:

  1. 使用 DelegatingTransactionAttribute 封装传入的 TransactionAttribute 实例。对于传入的 TransactionAttribute 类型的参数 txAttr ,当前实际类型是 RuleBasedTransactionAttribute,是由获取事务属性时生成的,主要用于数据承载,而这里之所以使用DelegatingTransactionAttribute 进行封装,也是为了提供更多的功能。
  2. 获取事务。即 tm.getTransaction(txAttr);,事务处理的核心当然是事务,这里获取到了事务。实际上getTransaction 方法返回的是 TransactionStatus (实现类是 DefaultTransactionStatus)。DefaultTransactionStatus 是对事务的进一步封装,包含了当前事务信息、挂起事务信息(如果有),保存点等信息。
  3. 构建事务信息。即prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); 。对上面几个步骤获取的信息构建 TransactionInfo 并返回。TransactionInfo 是 DefaultTransactionStatus 更进一步的封装。

我们来详细看看几个类的具体内容:

  1. 关于事务管理器 TransactionManager ,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。

  2. TransactionStatus (实际上的实现是 DefaultTransactionStatus) 里面包含的内容:
    在这里插入图片描述
    这里注意suspendedResources 实际上保存了是挂起的上层事务的信息。如果没有上层事务(也就是没嵌套事务),就是null,这里是通过UserProxyServiceImpl#findAll(事务传播属性是REQUIRED) 调用 UserServiceImpl#finaAll(事务传播属性是 REQUIRES_NEW) 的方式挂起来了一个来自 com.kingfish.springjdbcdemo.service.UserProxyServiceImpl.findAll 方法的事务信息。 savepoint 只有在内嵌事务的隔离级别是 PROPAGATION_NESTED 才有可能会保存。

  3. TransactionInfo 里面包含的内容:
    在这里插入图片描述

可以看到 TransactionInfoTransactionStatusTransactionAttributeTransactionManager 等属性更进一步封装。

  1. 关于事务挂起封装成的SuspendedResourcesHolder
    在这里插入图片描述

了解完上述一些类的保存内容后,下面我们来详细分析 createTransactionIfNecessary 中的几个方法

1. 获取事务 - tm.getTransaction(txAttr);

tm.getTransaction(txAttr); 实际上调用的是 AbstractPlatformTransactionManager#getTransaction 方法,在这里面获取了事务(可能是创建新事物,也可能不是),返回的类型是 TransactionStatus。

下面我们来看看其代码:

	@Override
	public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException {
    
    

		// Use defaults if no transaction definition given.
		TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
		// 1. 获取事务
		Object transaction = doGetTransaction();
		boolean debugEnabled = logger.isDebugEnabled();
		// 2. 判断当前线程是否存在事务,判断依据是当前线程记录的数据库连接不为空,且连接(connectionHolder)中的 transactionActive 属性 为true;
		// 这个方法的实现在 DataSourceTransactionManager#isExistingTransaction。
		if (isExistingTransaction(transaction)) {
    
    
			// Existing transaction found -> check propagation behavior to find out how to behave.	
			// 3.当前线程已经存在事务,则按照嵌套事务的逻辑处理
			return handleExistingTransaction(def, transaction, debugEnabled);
		}
		// 到这里就表明当前线程没有事务存在了,即不会出现嵌套事务的情况了
		// Check definition settings for new transaction.
		// 事务超时验证
		if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
    
    
			throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		// 下面是针对事务传播属性进行处理了
		// 4. 如果传播属性是 PROPAGATION_MANDATORY 。但是当前线程又不存在事务,则抛出异常
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
    
    
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		// 5. 如果传播属性是PROPAGATION_REQUIRED 、PROPAGATION_REQUIRES_NEW 、PROPAGATION_NESTED 都需要新建事务
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    
    
			// 5.1. 进行空挂起。为了记录原有事务的状态,便于后续操作对事务的恢复。
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
    
    
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
    
    
				// 5.2.开启事务,并返回了事务状态
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
    
    
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
    
    
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
    
    
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + def);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			// 6. 构建事务信息
			return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
		}
	}

可以看到基本逻辑如下:

  1. 获取事务 : 这里使用的是 DataSourceTransactionManager#doGetTransaction。创建基于JDBC 的事务实例。如果当前线程存在关于 dataSource 的连接,那么直接使用。这里有对保存点的一个设置,是否开启允许保存点取决于是否设置了允许嵌入式事务。
  2. 如果当前线程存在事务,则转向嵌套事务的处理
  3. 进行事务超时设置的验证
  4. 如果传播属性是 PROPAGATION_MANDATORY 。但是当前线程又不存在事务,则抛出异常
  5. 如果传播属性是PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEWPROPAGATION_NESTED 都需要新建事务,通过 startTransaction 开始事务构建
  6. 不满足上述传播属性,则使用 prepareTransactionStatus 来进行事务构建

对于一些隔离级别、timeout等功能的设置并不是Spring完成的,而是委托给底层的数据库连接去做的。


基于上面的逻辑我们再展开看一些方法:

1.1. doGetTransaction();

doGetTransaction(); 实际调用 DataSourceTransactionManager#doGetTransaction。目的就是获取事务对象。
其实现逻辑很简单 :如果当前线程存在关于 dataSource 的连接,那么直接使用。这里有对保存点的一个设置,是否开启允许保存点取决于是否设置了允许嵌入式事务。

	protected Object doGetTransaction() {
    
    
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		// 是否允许设置保存点 : 决定是否允许嵌套事务的存在
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		// 如果当前线程已经记录了数据库连接则使用原有连接
		ConnectionHolder conHolder =
				(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
		// false 代表非新创建连接
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

1.2. isExistingTransaction(transaction)

isExistingTransaction(transaction) 的实现是DataSourceTransactionManager#isExistingTransaction 中。其作用是判断当前线程是否存在事务,判断依据是当前线程记录的数据库连接不为空,且连接(connectionHolder)中的 transactionActive 属性为true

	protected boolean isExistingTransaction(Object transaction) {
    
    
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		// 存在连接,且其 transactionActive 属性不为空,isTransactionActive() 中的返回是  transactionActive = true 
		return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
	}

1.3 handleExistingTransaction(def, transaction, debugEnabled);

handleExistingTransaction(def, transaction, debugEnabled); 的实现在 AbstractPlatformTransactionManager#handleExistingTransaction 中,这里是为了处理嵌套事务,也就是说进入这一步则说明当前线程已经存在了事务。

这里面主要还是根据传播属性的不同而进行不同的逻辑处理。代码如下:

	private TransactionStatus handleExistingTransaction(
			TransactionDefinition definition, Object transaction, boolean debugEnabled)
			throws TransactionException {
    
    
		// 如果传播属性是 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) {
    
    

			// 挂起当前事务
			Object suspendedResources = suspend(transaction);
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			// 准备事务信息
			return prepareTransactionStatus(
					definition, null, false, newSynchronization, debugEnabled, suspendedResources);
		}
		// 如果传播属性是 PROPAGATION_REQUIRES_NEW,则需要挂起当前事务。新创建事务使用
		if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
    
    

			// 挂起当前事务,关于 suspend 方法,我们下面会讲
			SuspendedResourcesHolder suspendedResources = suspend(transaction);
			try {
    
    
				// 重新创建事务
				return startTransaction(definition, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error beginEx) {
    
    
				resumeAfterBeginException(transaction, suspendedResources, beginEx);
				throw beginEx;
			}
		}
			// 如果传播属性是 PROPAGATION_NESTED,则如果当前存在事务,则在嵌套事务内执行。否则自己创建事务
		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 (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.
				// 嵌入式事务的建立时便建立保存点
				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,这时候就需要新建事务
				return startTransaction(definition, transaction, debugEnabled, null);
			}
		}

		// 进行事务的合法性校验
		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);
	}

需要注意的是:

  • 对于 PROPAGATION_REQUIRES_NEW 传播属性,其表示当前方法必须要在他自己的事务中运行,一个新的事务被启动,所以原先的事务会被先挂起(suspend),挂起后作为当前事务 TransactionStatus 的一个属性(suspendedResources)存在。当当前事务执行结束后,再将原事务还原。

  • 对于 PROPAGATION_NESTED 传播属性,由于其需要的并非是全新的事务,而且当前事务的子事务。Spring考虑了两种情况

    • Spring中允许嵌套事务的时候,则首选设置保存点的方式作为异常处理的回滚。
    • 对于其他方式,比如 JTA 无法使用保存点的方式,那么处理方式和 PROPAGATION_REQUIRES_NEW相同,一旦出现异常,则由spring的事务异常处理机制去完成后续操作。

1.4. suspend(null);

suspend(null); 调用的是 AbstractPlatformTransactionManager#suspend 内容。其主要作用是为了记录原有事务的状态,便于后续操作对事务的恢复,实际上是将原事务的信息封装成 SuspendedResourcesHolder ,作为 TransactionStatus的一个属性存在。(我们这里的空挂起直接会返回null, 其他情况则会返回一个正常的SuspendedResourcesHolder)。

代码基本逻辑如下:

  1. 记录原事务信息
  2. 清除原事务信息
  3. 将原事务信息封装成 SuspendedResourcesHolder 返回,以便于外界恢复事务时使用

其代码如下:

	protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
    
    
		// 如果当前事务处于激活状态
		if (TransactionSynchronizationManager.isSynchronizationActive()) {
    
    
			List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
			try {
    
    
				Object suspendedResources = null;
				if (transaction != null) {
    
    
					// 这里主要将当前事务的数据源(ConnectionHolder)解绑
					suspendedResources = doSuspend(transaction);
				}
				// 获取当前事务name、readOnly、isolationLevel 、wasActive  等属性,封装成 SuspendedResourcesHolder 返回
				// 同时将当前事务的各种信息重置
				String name = TransactionSynchronizationManager.getCurrentTransactionName();
				TransactionSynchronizationManager.setCurrentTransactionName(null);
				boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
				TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
				Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
				boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
				TransactionSynchronizationManager.setActualTransactionActive(false);
				return new SuspendedResourcesHolder(
						suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
			}
			catch (RuntimeException | Error ex) {
    
    
				// doSuspend failed - original transaction is still active...
				doResumeSynchronization(suspendedSynchronizations);
				throw ex;
			}
		}
		// 如果当前事务并未激活且存在transaction 
		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;
		}
	}

1.5. startTransaction(def, transaction, debugEnabled, suspendedResources);

startTransaction(def, transaction, debugEnabled, suspendedResources); 的实现在AbstractPlatformTransactionManager#startTransaction 中,主要作用是创建新的事务。

经历了上面几步处理,到达这一步的时候说明,当前已经不是嵌套事务。

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

		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		// 创建一个默认的DefaultTransactionStatus 
		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
		// 构造 Transaction,包括设置 ConnectionHolder、隔离级别、timeout。
		// 并且如果是新连接,则绑定当当前线程。
		doBegin(transaction, definition);
		// 新同步事务的设置,针对于当期线程的设置
		prepareSynchronization(status, definition);
		return status;
	}

我们详细看下面两个方法:

1.5.1. doBegin(transaction, definition);

doBegin(transaction, definition); 的实现在 DataSourceTransactionManager#doBegin 中。
这里的目的是 为了构造 transaction,包括设置 ConnectionHolder、隔离级别、timeout。如果是新连接,则绑定到当前线程。

	protected void doBegin(Object transaction, TransactionDefinition definition) {
    
    
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
		Connection con = null;

		try {
    
    	
			// 如果当前线程中的数据库连接不存在,或者事务同步为true的情况下需要重新获取数据库连接
			if (!txObject.hasConnectionHolder() ||
					txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
    
    
				Connection newCon = obtainDataSource().getConnection();
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
				}
				txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
			}
			// 将同步标识设置为 true
			txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
			con = txObject.getConnectionHolder().getConnection();
			// 设置事务隔离级别
			Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
			txObject.setPreviousIsolationLevel(previousIsolationLevel);
			txObject.setReadOnly(definition.isReadOnly());

			// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
			// so we don't want to do it unnecessarily (for example if we've explicitly
			// configured the connection pool to set it already).
			// 更改自动提交,将数据库的自动提交改为 Spring 来控制
			if (con.getAutoCommit()) {
    
    
				txObject.setMustRestoreAutoCommit(true);
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
				}
				con.setAutoCommit(false);
			}
			// 准备事务连接,这里实际上执行了 SET TRANSACTION READ ONLY 的sql 语句
			prepareTransactionalConnection(con, definition);
			// 设置当前线程已经存在事务,这个 transactionActive 属性是判断是否当前线程存在事务的依据
			txObject.getConnectionHolder().setTransactionActive(true);
			// 设置超时时间
			int timeout = determineTimeout(definition);
			if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    
    
				txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
			}

			// Bind the connection holder to the thread.
			// 如果是新的连接,则绑定数据库连接到当前线程
			if (txObject.isNewConnectionHolder()) {
    
    
				TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
			}
		}

		catch (Throwable ex) {
    
    
			if (txObject.isNewConnectionHolder()) {
    
    
				DataSourceUtils.releaseConnection(con, obtainDataSource());
				txObject.setConnectionHolder(null, false);
			}
			throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
		}
	}
	
	...

	protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)
			throws SQLException {
    
    

		if (isEnforceReadOnly() && definition.isReadOnly()) {
    
    
			try (Statement stmt = con.createStatement()) {
    
    
				// 设置事务为只读
				stmt.executeUpdate("SET TRANSACTION READ ONLY");
			}
		}
	}

可以说事务是从这个函数开始的,因为在这个函数中已经开始尝试对数据库连接的获取,并在在获取数据库连接的同时,也进行了一些必要的设置的同步。

  1. 尝试获取连接。但并不是每一次都会获取新的连接,如果当前线程中的 ConnectionHolder 已经存在,则没必要再次获取,或者对事物同步表示设置为true的需要重新获取连接。
  2. 设置隔离级别和只读标识
  3. 更改事务的默认提交设置。
    如果事务属性是自动提交,那么需要改变这个设置,将操作委托给Spring来处理。
  4. 设置标志位,标识当前连接已经被事务激活。
  5. 设置过期时间
  6. ConnectionHolder 绑定到当前线程

我们这里额外看一下 DataSourceUtils.prepareConnectionForTransaction 方法是如何设置隔离级别的

	public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)
			throws SQLException {
    
    

		Assert.notNull(con, "No Connection specified");

		boolean debugEnabled = logger.isDebugEnabled();
		// Set read-only flag.
		// 设置属性只读
		if (definition != null && definition.isReadOnly()) {
    
    
			try {
    
    
				if (debugEnabled) {
    
    
					logger.debug("Setting JDBC Connection [" + con + "] read-only");
				}
				con.setReadOnly(true);
			}
			catch (SQLException | RuntimeException ex) {
    
    
				Throwable exToCheck = ex;
				while (exToCheck != null) {
    
    
					if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
    
    
						// Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0
						throw ex;
					}
					exToCheck = exToCheck.getCause();
				}
				// "read-only not supported" SQLException -> ignore, it's just a hint anyway
				logger.debug("Could not set JDBC Connection read-only", ex);
			}
		}

		// Apply specific isolation level, if any.
		// 设置数据库隔离级别
		Integer previousIsolationLevel = null;
		if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
    
    
			if (debugEnabled) {
    
    
				logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
						definition.getIsolationLevel());
			}
			int currentIsolation = con.getTransactionIsolation();
			if (currentIsolation != definition.getIsolationLevel()) {
    
    
				previousIsolationLevel = currentIsolation;
				con.setTransactionIsolation(definition.getIsolationLevel());
			}
		}

		return previousIsolationLevel;
	}

可以看到,在 DataSourceUtils.prepareConnectionForTransaction 方法中并没有什么复杂的逻辑,因为其主要实现都交由更底层的 数据库API 来完成了。

1.5.2 prepareSynchronization(status, definition);

prepareSynchronization(status, definition); 的实现在AbstractPlatformTransactionManager#prepareSynchronization 中。其目的是将事务信息记录到当前线程中,逻辑很简单,这里不再赘述。

	protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
    
    
		// 如果是新的事务,则需要同步信息
		if (status.isNewSynchronization()) {
    
    
			// 下面是对事务的信息的记录,记录到当前线程中。
			TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
					definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
							definition.getIsolationLevel() : null);
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
			TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
			TransactionSynchronizationManager.initSynchronization();
		}
	}

2. 构建事务信息 - prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);

prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); 的实现是在TransactionAspectSupport#prepareTransactionInfo 中。

当已经建立事务连接并完成了事务信息的提取后,我们需要将所有的事务信息统一记录在 TransactionInfo 类型的实例中,这个实例包含了目标方法开始前的所有状态信息,一旦事务执行失败,Spring会通过 TransactionInfo 类实例中的信息来进行回滚等后续工作。

详细代码如下:


	protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, String joinpointIdentification,
			@Nullable TransactionStatus status) {
    
    
		// 封装成 TransactionInfo  实例
		TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
		if (txAttr != null) {
    
    

			// 记录事务状态
			txInfo.newTransactionStatus(status);
		}
		else {
    
    

		}
		// 绑定到线程当前上
		txInfo.bindToThread();
		return txInfo;
	}

三、总结

TransactionAspectSupport#createTransactionIfNecessary 的功能是根据需要创建事务。这里面考虑到嵌套事务的情况,并对事务的传播属性进行了相应的处理,最终处理后。返回的是一个 TransactionInfo 的值,里面封装了事务的各种信息,供给后面的回滚或者提交使用。


以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

猜你喜欢

转载自blog.csdn.net/qq_36882793/article/details/107299307
今日推荐