面试官:问你一个,Spring事务是如何传播的?( 五 )

这里明显是进入第一个if并且会调用到doSuspend方法 , 整体来说挂起事务很简单:首先将DataSourceTransactionObject的ConnectionHolder设置为空并解除与当前线程的绑定 , 之后将解除绑定的ConnectionHolder和其它属性(事务名称、隔离级别、只读属性)通通封装到SuspendedResourcesHolder对象 , 并将当前事务的活动状态设置为false 。 挂起事务之后又通过newTransactionStatus创建了一个新的事务状态并调用doBegin开启事务 , 这里不再重复分析 。 接着来看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 (debugEnabled) {logger.debug("Creating nested transaction with name [" + definition.getName() + "]");}//默认是可以嵌套事务的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.boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}}这里面可以看到如果允许嵌套事务 , 就会创建一个DefaultTransactionStatus对象(注意newTransaction是false , 表明不是一个新事务)和回滚点;如果不允许嵌套 , 就会创建新事务并开启 。 当上面的判断都不满足时 , 也就是传播属性为默认PROPAGATION_REQUIRED时 , 则只是创建了一个newTransaction为false的DefaultTransactionStatus返回 。 完成之后又是调用proceedWithInvocation , 那么就是执行B类的addB方法 , 假如没有发生异常 , 那么就会回到切面调用commitTransactionAfterReturning提交addB的事务:
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {if (txInfo != null} } public final void commit(TransactionStatus status) throws TransactionException {processCommit(defStatus); } private void processCommit(DefaultTransactionStatus status) throws TransactionException {try {boolean beforeCompletionInvoked = false;try {boolean unexpectedRollback = false;prepareForCommit(status);triggerBeforeCommit(status);triggerBeforeCompletion(status);beforeCompletionInvoked = true;if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Releasing transaction savepoint");}// 如果是nested , 没有提交 , 只是将savepoint清除掉了unexpectedRollback = status.isGlobalRollbackOnly();status.releaseHeldSavepoint();}//如果都是PROPAGATION_REQUIRED , 最外层的才会走进来统一提交 , 如果是PROPAGATION_REQUIRES_NEW , 每一个事务都会进来else if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction commit");}unexpectedRollback = status.isGlobalRollbackOnly();doCommit(status);}else if (isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = status.isGlobalRollbackOnly();}}}finally {cleanupAfterCompletion(status);} }主要逻辑在processCommit方法中 。 如果存在回滚点 , 可以看到并没有提交事务 , 只是将当前事务的回滚点清除了;而如果是新事务 , 就会调用doCommit提交事务 , 也就是只有PROPAGATION_REQUIRED属性下的最外层事务和PROPAGATION_REQUIRES_NEW属性下的事务能提交 。 事务提交完成后会调用cleanupAfterCompletion清除当前事务的状态 , 如果有挂起的事务还会通过resume恢复挂起的事务(将解绑的连接和当前线程绑定以及将之前保存的事务状态重新设置回去) 。 当前事务正常提交后 , 那么就会轮到addA方法提交 , 处理逻辑同上 , 不再赘述 。 如果调用addB发生异常 , 就会通过completeTransactionAfterThrowing进行回滚:
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {if (txInfo != null}if (txInfo.transactionAttribute != null}}} } public final void rollback(TransactionStatus status) throws TransactionException {DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;processRollback(defStatus, false); } 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();}//都为PROPAGATION_REQUIRED最外层事务统一回滚else if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction rollback");}doRollback(status);}else {// Participating in larger transactionif (status.hasTransaction()) {if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {if (status.isDebug()) {logger.debug("Participating transaction failed - marking existing transaction as rollback-only");}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 earlyif (!isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = false;}}}catch (RuntimeException | Error ex) {triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);throw ex;}triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);// Raise UnexpectedRollbackException if we had a global rollback-only markerif (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");}}finally {cleanupAfterCompletion(status);} }