Spring源码深度解析:二十、事务实现④ - 事务的回滚

一、前言

文章目录:Spring源码深度解析:文章目录

Spring源码深度解析:十八、事务实现② - ProxyTransactionManagementConfiguration中,解析了关事务的代理创建过程,同时在上篇中讲述了 事务的创建过程,本文就开始讲解一下 事务的回滚过程。

二、事务的回滚 - completeTransactionAfterThrowing

Spring源码深度解析:十八、事务实现② - ProxyTransactionManagementConfiguration中我们分析了TransactionAspectSupport#invokeWithinTransaction方法完成了事务的增强调用 ,而completeTransactionAfterThrowing的实现是 TransactionAspectSupport#completeTransactionAfterThrowing。在出现异常的时候,通过该方法进行事务的回滚。

下面我们来看具体代码实现 :

	protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		// 判断当前线程中是否存在事务
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
						"] after exception: " + ex);
			}
			// 判断是否触发回滚操作,这里的条件是异常是否是 RuntimeException 或 error类型
			// 即 return (ex instanceof RuntimeException || ex instanceof Error);
			if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
				try {
					// 执行回滚操作
					txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
			}
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				try {
					// 如果不满足回滚条件,则还是会提交,也就是说如果抛出异常不是 RuntimeException 或 error类型,则不会触发事务的回滚。
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
			}
		}
	}

上面可以看到,触发回滚操作的判断条件是

txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)

txInfo.transactionAttribute != null 自不必说,表示必须存在事务属性信息,即是事务方法。txInfo.transactionAttribute.rollbackOn(ex)默认的实现如下:

	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

也就是说只有当异常类型是 Error或者 RuntimeException 才会进行回滚。
这里需要注意,如果抛出了其他类型异常,那么并不代表事务没有启用,而是回滚没有触发。

我们下面看看回滚和提交的具体操作:

1. AbstractPlatformTransactionManager#rollback

我们下面来看看txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); 回滚的具体操作即即 中的AbstractPlatformTransactionManager#rollback方法

	@Override
	public final void rollback(TransactionStatus status) throws TransactionException {
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		// 执行回滚
		processRollback(defStatus, false);
	}
	
	....
	
	// 省略了日志打印
	private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;

			try {
				// 激活所有 TransactionSynchronization 中对应的方法 beforeCompletion() 方法
				triggerBeforeCompletion(status);
				// 如果当前事务有保存点,也就是当前事务为单独的线程则会退到保存点
				if (status.hasSavepoint()) {
					status.rollbackToHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					// 如果当前事务为独立的新事物,则直接回退
					doRollback(status);
				}
				else {
					// Participating in larger transaction
					if (status.hasTransaction()) {
						if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
							// 如果当前事务不是独立的事务,那么只能标记状态,等到事务链执行完毕后统一回滚
							doSetRollbackOnly(status);
						}
						else {
						
						}
					}
					else {
					}
					// 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;
			}
			// 激活所有 TransactionSynchronization 中对应的方法 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 {
			// 清空记录的资源并将挂起的资源恢复
			cleanupAfterCompletion(status);
		}
	}

我们可以简单总结一下整个脉络:

  1. 首先是自定义触发器的调用,这里是Spring提供的回滚扩展点,包括在回滚前、回滚成功、回滚失败的触发器调用,自定义触发器会根据这些信息作出进一步的处理。而对于触发器的注册,常见的是在回调过程中通过TransactionSynchronizationManager#registerSynchronization方法完成,
  2. 除了触发监听函数外,就是真正的回滚逻辑处理了,而这里的具体回滚操作都委托给了底层数据库连接提供的API 来操作的。回滚的基本策略是:
    1、当之前已经保存的事务信息中有保存点信息的时候,使用保存点信息回滚。常用于嵌入式事务,对于嵌入式事务的处理,内嵌的事务异常并不会引起外部事物的回滚。
    2、对于之前没有保存的事务信息中的事务为新事物,那么直接回滚。常用语单独事务的处理,对于没有保存点的回滚,Spring同样是使用底层数据库连接提供的API 来操作的。我们这里则是 DataSourceTransactionManager#doRollback
    3、当前事务信息标明是存在事务的,又不属于以上两种情况,多数用于 JTA,只做回滚标识,等到提交的时候统一不提交。
  3. 回滚后的信息清除。对于回滚逻辑执行结束后,无论回滚是否陈宫,都必须要将信息清除。

下面我们来详细看看上面三点:

1.1. 自定义触发器的调用

自定义触发器的调用,包括在回滚前、回滚成功、回滚失败的触发器调用。实现逻辑都类似,我们这里挑一个回滚前的触发器看一看逻辑:

	protected final void triggerBeforeCompletion(DefaultTransactionStatus status) {
		if (status.isNewSynchronization()) {
			if (status.isDebug()) {
				logger.trace("Triggering beforeCompletion synchronization");
			}
			// 触发前置操作
			TransactionSynchronizationUtils.triggerBeforeCompletion();
		}
	}

TransactionSynchronizationUtils.triggerBeforeCompletion(); 的代码如下:

	public static void triggerBeforeCompletion() {
		// 获取所有TransactionSynchronization 触发 beforeCompletion 方法
		for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
			try {
				synchronization.beforeCompletion();
			}
			catch (Throwable tsex) {
				logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex);
			}
		}
	}

triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); 与之逻辑类似,这里就不再看了。

1.2. 回滚逻辑处理

回滚操作最底层的操作都是委托给了数据库API 来实现,所以这里并没有负责的逻辑。

回滚逻辑上面也说了有三种情况,下面我们来一个一个看一看:

1.2.1. status.rollbackToHeldSavepoint();

status.rollbackToHeldSavepoint(); 的实现在 AbstractTransactionStatus#rollbackToHeldSavepoint中。

处理情况是 :当之前已经保存的事务信息中有保存点信息的时候,使用保存点信息回滚。常用于嵌入式事务,对于嵌入式事务(并非是嵌套service事务)的处理,内嵌的事务异常并不会引起外部事物的回滚。

	public void rollbackToHeldSavepoint() throws TransactionException {
		Object savepoint = getSavepoint();
		if (savepoint == null) {
			throw new TransactionUsageException(
					"Cannot roll back to savepoint - no savepoint associated with current transaction");
		}
		// 回滚到 savepoint 
		getSavepointManager().rollbackToSavepoint(savepoint);
		// 释放保存点
		getSavepointManager().releaseSavepoint(savepoint);
		setSavepoint(null);
	}

这里使用的是 JDBC 的方式进行数据库了连接,所以这里调用的是getSavepoint(); 返回的是JdbcTransactionObjectSupport类型。所以这里我们来看 JdbcTransactionObjectSupport 中的

	@Override
	public void rollbackToSavepoint(Object savepoint) throws TransactionException {
		ConnectionHolder conHolder = getConnectionHolderForSavepoint();
		try {
			// 直接调用 Connection的rollback 方法
			conHolder.getConnection().rollback((Savepoint) savepoint);
			conHolder.resetRollbackOnly();
		}
		catch (Throwable ex) {
			throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);
		}
	}
	@Override
	public void releaseSavepoint(Object savepoint) throws TransactionException {
		ConnectionHolder conHolder = getConnectionHolderForSavepoint();
		try {
			// 直接调用 Connection的releaseSavepoint方法
			conHolder.getConnection().releaseSavepoint((Savepoint) savepoint);
		}
		catch (Throwable ex) {
			logger.debug("Could not explicitly release JDBC savepoint", ex);
		}
	}

1.2.2. doRollback(status);

doRollback(status); 的实现在DataSourceTransactionManager#doRollback中。

处理情况是 :对于之前没有保存的事务信息中的事务为新事物,那么直接回滚。常用语单独事务的处理,对于没有保存点的回滚,Spring同样是使用底层数据库连接提供的API 来操作的。

	@Override
	protected void doRollback(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		Connection con = txObject.getConnectionHolder().getConnection();
		if (status.isDebug()) {
			logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
		}
		try {
			// 直接调用rollback方法 回滚
			con.rollback();
		}
		catch (SQLException ex) {
			throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
		}
	}

1.2.3. doSetRollbackOnly(status);

doSetRollbackOnly(status); 的实现在DataSourceTransactionManager#doSetRollbackOnly中。

处理情况: 当前事务信息标明是存在事务的,又不属于以上两种情况,多数用于 JTA(Java Transaction API ),只做回滚标识,等到提交的时候统一不提交。即当外围事务进行提交时,发现内嵌的事务中 rollbackOnly 被设置成true。则直接进行回滚,而不再尝试提交

	@Override
	protected void doSetRollbackOnly(DefaultTransactionStatus status) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
		if (status.isDebug()) {
			logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() +
					"] rollback-only");
		}
		txObject.setRollbackOnly();
	}

1.3. 回滚信息清除

cleanupAfterCompletion(status); 的实现在 AbstractPlatformTransactionManager#cleanupAfterCompletion中。
其作用是完成事务回滚的收尾工作,主要包括以下内容 :

  1. 设置状态是对事务信息做完成标识以避免重复调用
  2. 如果当前事务是新的同步状态,需要将绑定到当前线程的事务信息清除
  3. 如果是新事物需要做出清除资源的工作

我们来看看下面的两个方法:

1.3.1. doCleanupAfterCompletion(status.getTransaction());

doCleanupAfterCompletion(status.getTransaction()); 的实现在DataSourceTransactionManager#doCleanupAfterCompletion中。在这里完成事务回滚的收尾工作。

	@Override
	protected void doCleanupAfterCompletion(Object transaction) {
		DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

		// Remove the connection holder from the thread, if exposed.
		if (txObject.isNewConnectionHolder()) {
			// 将数据库连接从当前线程中解除绑定
			TransactionSynchronizationManager.unbindResource(obtainDataSource());
		}
		// Reset connection.
		// 释放连接
		Connection con = txObject.getConnectionHolder().getConnection();
		try {
			if (txObject.isMustRestoreAutoCommit()) {
				// 恢复数据库连接自动提交属性
				con.setAutoCommit(true);
			}
			// 重置数据连接
			DataSourceUtils.resetConnectionAfterTransaction(
					con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
		}
		catch (Throwable ex) {
			logger.debug("Could not reset JDBC Connection after transaction", ex);
		}

		if (txObject.isNewConnectionHolder()) {
			if (logger.isDebugEnabled()) {
				logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
			}
			// 如果当前事务是独立的新创建的事务则在事务完成时释放数据库连接。
			DataSourceUtils.releaseConnection(con, this.dataSource);
		}

		txObject.getConnectionHolder().clear();
	}

1.3.2. resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());

如果在事务执行前有事务挂起,那么当前事务执行结束后需要将挂起的事务恢复。

	protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
			throws TransactionException {

		if (resourcesHolder != null) {
			// 获取挂起的事务
			Object suspendedResources = resourcesHolder.suspendedResources;
			if (suspendedResources != null) {
				// 恢复事务
				doResume(transaction, suspendedResources);
			}
			List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
			// 设置事务属性
			if (suspendedSynchronizations != null) {
				TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
				TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
				TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
				doResumeSynchronization(suspendedSynchronizations);
			}
		}
	}

从上面整个回滚流程可以看到,很多的操作,Spring都交给数据库底层实现了,Spring仅仅起到了一个管理的作用。

三、总结

事务的回滚操作,根据不同的情况执行不同的回滚策略。并且在回滚结束之后恢复了挂起的事务(如果有挂起事务)。

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值