第19章 Spring事务王国的架构(五)

第19章 Spring事务王国的架构

19.2 和平年代

19.2.2 TransactionStatus

2. 窥一斑而知全豹

当然,这期间也会涉及事务定义的应用以及条件检查等逻辑。在所有一切搞定之后,newTransactionStatus会创建一个包含definition、transaction object以及挂起的事务信息和其他状态信息的DefaultTransactionStatus实例并返回。

如果definition定义的传播行为是PROPAGATION_NESTED,根据情况创建嵌套事务,如通过Savepoint或者JTA的TransactionManager。如下:

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() + "]");
  }
	if(useSavepointForNesteaTransaction()) {
    
    
		// Create savepoint within existing Spring-managed transaction,
		// through the SavepointManager API implemented by TransactionStatus.
		// Usually uses JDBC3.0 savepoints. Never activates Spring synchronization.
		DefaultTransactionStatus status = newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
    status.createAndHoldSavepoint();
		return status;
  }
	else {
    
    
		// Nested transaction through nested begin and commit/rollback ca11s.
		// Usually only for JTA: Spring synchronization might get activated here
		// in case of a pre-existing JTA transaction.
		doBegin(transaction, definition);
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
  }
}

在这种情况下,会首先通过isNestedTransactionAllowed()方法检查AbstractPlatformTransactionManager的nestedTransactionAllowea属性状态。如果允许嵌套事务,那么还得分两种情况处理。对于DataSourceTransactionManager来说,因为它支持使用Savepoint创建嵌套事务,所以,会使用TransactionStatus创建相应的Savepoint并返回。而JtaTransactionManager则要依赖于具体JTA产品的TransactionManager提供嵌套事务支持。

useSavepointForNestedTransaction()方法默认返回true,即默认使用Savepoint创建嵌套事务。如果具体子类不支持使用Savepoint创建嵌套事务,则需要覆写该方法,如JtaTransactionManager。如果需要检查事务状态匹配情况,则对当前存在事务与传入的defintion中定义的隔离级别与ReadOnly属性进行检查,如果数据不吻合,则抛出异常。如下:

if(isValidateExistingTransaction()) {
    
    
	// validate isolation
	// validate readonly
}

AbstractPlatformTransactionManager的validateExistingTransaction属性默认值为false。如果你想进一步加强事务属性之间的一致性,可以将validateExistingTransaction属性设置为true,那么这时,以上代码即会被触发执行。剩下的就是在其他情况下,直接构建TransactionStatus返回。比如对应PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED的情况。

(b)如果isExistingTransaction(transaction)方法返回false,即不存在当前事务的情况下。当definition中定义的传播行为是PROPAGATION_MANDATORY的时候,抛出异常。因为不存在当前事务,所以根据PROPAGATION_MANDATORY的语义,理当如此。当definition中定义的传播行为是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW或者PROPAGATION_NESTED的时候,开启新的事务。如下:

if(definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES__NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    
    
	SuspendedResourcesHolder suspendedResources = suspend(null);
	if(debugEnabled) {
    
    
		logger.debug("Creating new transaction with name[" + definition.getName() +"]: " + definition);
  }
  try {
    
    
		doBegin(transaction, definition);
  }
	catch(TransactionException ex) {
    
    
		resume(null, suspendedResources);
    throw ex;
  }
	boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
	return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
}

之所以在doBegin之前先调用传入null的suspend()方法,是因为考虑到如果有注册的Synchronization,需要暂时将这些与将要开启的新事务无关的Synchronization先放一边。

剩下的其他情况,则返回不包含任何transaction object的TransactionStatus。这种情况下虽然是空的事务,但有可能需要处理在事务过程中相关的Synchronization。

从getTransaction(TransactionDefinition)的逻辑可以看出,AbstractPlatformTransactionManager更多关注的是事务处理过程中的总体逻辑,而跟具体事务资源相关的处理则交给了具体的子类来实现。

事务处理的完成有两种情况,即回滚事务或者提交事务,AbstractPlatformTransactionManager提供的rollback(TransactionStatus)commit(TransactionStatus)两个模板方法,分别对应这两种情况下的处理。因为事务提交过程中可能需要处理回滚逻辑,我们不妨以commit(TransactionStatus)的实现流程看一下AbstractPlatformTransactionManager是如何处理事务完成的。

(1)因为在事务处理过程中,我们可以通过TransactionStatus的setRollbackOnly()方法标记事务回滚,所以,commit(TransactionStatus)在具体提交事务之前会检查rollBackOnly状态。如果该状态被设置,那么转而执行事务的回滚操作。

rollback(TransactionStatus)的逻辑主要包含如下3点。

  • 回滚事务。这里的回滚事务又可分为如下3种情况。

    如果是嵌套事务,则通过TransactionStatus释放Savepoint。
    如果TransactionStatus表示当前事务是一个新的事务,则调用子类的doRollback(TransactionStatus)方法真正的回滚事务。doRollback(TransactionStatus)是抽象方法,具体子类必须实现它。DataSourceTransactionManager在实现该方法的时候,无疑是调用connection.rollback()。HibernateTransactionManager会通过它Session上的Transaction的rollback()方法回滚事务。其他子类对doRollback(Transactionstatus)的实现逻辑依此类推。
    如果当前存在事务,并且rollbackOnly状态被设置,则调用由子类实现的doSetRollbackOnly(TransactionStatus)方法,各子类实现通常会将当前的transactionobject的状态设置为rollBackOnly。

  • 触发Synchronization事件。回滚时触发的事件比提交时触发的事件要少,只有triggerBeforeCompletion(status)triggerAfterCompletion()

  • 清理事务资源。如下所述。

    设置TransactionStatus中的completed为完成状态。

    清理与当前事务相关的Synchronization。

    调用doCleanupAfterCompletion()释放事务资源,并解除到TransactionSynchronizationManager的资源绑定。对于DataSourceTransactionManager来说,是关闭数据库连接,然后解除对DataSource对应资源的绑定。

    如果之前有挂起的事务,恢复挂起的事务。

(2)如果rollBackOnly状态没被设置,则执行正常的事务提交操作。

commit(TransactionStatus)方法的其他逻辑与rollback(TransactionStatus)方法基本相似,只是几个具体操作有所差别,如下所述。

  • 回滚事务现在变成是提交事务。提交事务的时候,也会涉及如下几种情况。
    • 决定是否提前检测全局的rollBackOnly标志。如果最外层事务已经被标记为rollBackOnly,并且failEarly0nGlobalRollback0nly为true,则抛出异常(如代码清单19-5所示)。

    • 如果提交事务之前发现TransactionStatus持有Savepoint,则释放它。这实际上是在处理嵌套事务的提交。

    • 如果TransactionStatus表示要提交的事务是一个新的事务,则调用子类的doCommit(TransactionStatus)方法实现提交事务。doCommit(TransactionStatus)也是AbstractPlatformTransactionManager公开给子类实现的抽象方法,子类必须实现该方法。对于DataSourceTransactionManager来说,因为事务的提交由Connection决定,所以会直接调用connection.commit()提交事务。其他的子类也会使用自身的局部事务API在该方法中实现事务的提交。

  • 需要触发Synchronization相关事件。不过,触发的事件比rollback(TransactionStatus)中的要多,包括triggerBeforeCommit()、trinrorRoforeCompletion()、triggerAfterCommit()和triggerAfterCompletion()。
  • 如果AbstractPlatformTransactionManager的rollbackOnCommitFailure状态被设置为true,则表示如果在事务提交过程中出现异常,需要回滚事务。所以,当commit(TransactionStatus)方法捕获相应异常,并且检测到该字段被设置的时候,需要回滚事务。rollbackOnCommitFailure的默认值是false,表示即使提交过程中出现异常,也不回滚事务。
  • 既然commit(TransactionStatus)rollback(TransactionStatus)一样,都是意味着事务的完成,那么也需要在最后进行事务资源清理的工作,具体内容可以参照rollback(TransactionStatus)部分。

DataSourceTransactionManager事务提交部分代码摘录:

boolean globalRollbackOnly = false;
if(status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
    
    
	globalRollbackOnly = status.isGlobalRollbackOnly();
}
...
if(globalRollbackOnly) {
    
    
	throw new UnexpectedRollbackException("Transaction silently rolled back because it has been marked as rollback-only");
}

suspend和resume两个方法的逻辑更好理解了。前者会把TransactionSynchronizationManager上当前事务对应的Synchronization信息以及资源获取到SuspendedResourcesHolder中,然后解除这些绑定。后者则会将SuspendedResourcesHolder中保持的信息重新绑定到TransactionSynchronizationManager。

实际上,如果将AbstractPlatformTransactionManager中处理Synchroniaztion回调以及事务传播行为的逻辑剥离一下的话,就会发现,整个的逻辑流程就是本章开始部分展示的实现原型所表达的那样。

图19-11展示了AbstractPlatformTransactionManager需要子类实现或者覆写的方法。对于各个子类来说,无非就是根据自身需要管理的资源和事务管理API提供这些方法的实现而已。

image-20220611121732365

19.3 小结

为了帮助你更容易地理解整个Spring事务管理抽象框架的设计和实现,我们在统一中原的过程中推导并实现了相应的PlatformTransactionManager以及相关原型实现。随后,在原型的基础上,对Spring事务管理抽象框架中的主要类进行了介绍和分析,尤其是对DataSourceTransactionManager的实现做了剖析,以期能够“以点带面”地帮助大家更好地理解Spring事务管理抽象框架中各PlatformTransactionManager通用的实现原理。

在了解了以上内容之后,我们将一起了解一下,在实际开发过程中,如何使用Spring的事务管理抽象框架让我们的日常开发生活变得更加美好。

猜你喜欢

转载自blog.csdn.net/qq_34626094/article/details/125358339