Spring事务源码(三)

Spring事务源码(三)

本文主要讲述Spring事务在获得事务对象后,再执行目标对象的原生方法后遇到异常后,如何处理事务。通常是通过completeTransactionAfterThrowing这个方法解决



前言

我们需要回到TransactionAspectSupport#invokeWithinTransaction这个方法来,前两篇文章都是讲诉如何获得TransactionStatus,最后封装成TransactionInfo。 而本文是讲述获得事务后,执行完目标方法后catch到异常后该怎么处理。 这里主要解析completeTransactionAfterThrowing

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
			throws Throwable {

	// If the transaction attribute is null, the method is non-transactional.
	final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
		Object retVal = null;
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
			// target invocation exception
			// 捕获异常后如何处理
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
			//清除当前方法上的事务信息
			cleanupTransactionInfo(txInfo);
		}
		// 如果执行到这里说明没有异常,尝试提交事务
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}
}

一、completeTransactionAfterThrowing

执行目标方法后该怎么处理事务

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
	if (txInfo != null && txInfo.hasTransaction()) {
		// 判断目标方法的事务属性针对此异常是否需要回滚事务
		if (txInfo.transactionAttribute.rollbackOn(ex)) {
			try {
				// 取出事务管理器,回滚当前事务
				txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
			}
			catch (TransactionSystemException ex2) {
				throw ex2;
			}
			catch (RuntimeException ex2) {
				throw ex2;
			}
			catch (Error err) {
				throw err;
			}
		}
		else {
			// We don't roll back on this exception.
			// Will still roll back if TransactionStatus.isRollbackOnly() is true.
			// 如果目标方法的事务属性判断不回滚事务,那么提交事务
			try {
				txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
			}
			catch (TransactionSystemException ex2) {
				logger.error("Application exception overridden by commit exception", ex);
				ex2.initApplicationException(ex);
				throw ex2;
			}
			catch (RuntimeException ex2) {
				logger.error("Application exception overridden by commit exception", ex);
				throw ex2;
			}
			catch (Error err) {
				logger.error("Application exception overridden by commit error", ex);
				throw err;
			}
		}
	}
}

transactionAttribute的rollbackOn方法

这个方法判断Spring事务针对此异常是回滚还是该提交,具体实现类的方式是RuleBasedTransactionAttribute#rollbackOn

@Override
public boolean rollbackOn(Throwable ex) {
	RollbackRuleAttribute winner = null;
	int deepest = Integer.MAX_VALUE;
	if (this.rollbackRules != null) {
		// 循环遍历rollbackRules,真实是先遍历RollbackRuleAttribute,然后在遍历NoRollbackRuleAttribute
		// 总之就是要找出符合要求(ex.getName().contains(rule.exceptionName))
		// 且层级次数最低的那个Rule
		for (RollbackRuleAttribute rule : this.rollbackRules) {
			int depth = rule.getDepth(ex);
			if (depth >= 0 && depth < deepest) {
				deepest = depth;
				winner = rule;
			}
		}
	}

	// User superclass behavior (rollback on unchecked) if no rule matches.
	// 如果没有找着符合要求的Rule,则调用父类的rollbackOn判断
	// 判断当前ex是RuntimeException 或是Error
	if (winner == null) {
		logger.trace("No relevant rollback rule found: applying default rules");
		// return (ex instanceof RuntimeException || ex instanceof Error);
		return super.rollbackOn(ex);
	}
	// 如果有找到合适的Rule
	// 则判断是RollbackRuleAttribute 则需要回滚是否
	// 是NoRollbackRuleAttribute 则不需要回滚
	return !(winner instanceof NoRollbackRuleAttribute);
}

// RollbackRuleAttribute的方法: 获取支持的异常以及层级次数
// depth默认是0, 如果RollbackRuleAttribute的exceptionClass包含此异常,直接返回depth
// 如果是Throwable返回-1
// 否则拿exceptionClass的父类递归查找
private int getDepth(Class<?> exceptionClass, int depth) {
	if (exceptionClass.getName().contains(this.exceptionName)) {
		// Found it!
		return depth;
	}
	// If we've gone as far as we can go and haven't found it...
	if (exceptionClass == Throwable.class) {
		return -1;
	}
	return getDepth(exceptionClass.getSuperclass(), depth + 1);
}

二、PlatformTransactionManager的rollback

如果在上一步transactionAttribute.rollbackOn(ex)判断需要回滚事务,则调用事务管理器的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);
}

private void processRollback(DefaultTransactionStatus status) {
	try {
		try {
			// 主要是循环遍历事务同步管理器的synchronization.beforeCompletion
			// 当前还得判断是当前方法里是开启了新事务
			triggerBeforeCompletion(status);
			// 如果是通过传播方式Nested,加入了savePoint,则回滚到创建savePoint那里
			if (status.hasSavepoint()) {
				if (status.isDebug()) {
					logger.debug("Rolling back transaction to savepoint");
				}
				status.rollbackToHeldSavepoint();
			}
			// 如果是新事物,即开启了事务,则真正去回滚事务
			else if (status.isNewTransaction()) {
				if (status.isDebug()) {
					logger.debug("Initiating transaction rollback");
				}
				// 从status.getTransaction()取出事务对象,再取出connection,通过调用connection.rollback()方法来实现
				// 回滚事务
				doRollback(status);
			}
			// 如果目标方法上有事务,但不是新事务,判断一下是否需要标记回滚事务
			// 如果是需要回滚的,则将事务对象标记为需要回滚,等待创建事务的方法再回滚事务
			// 否则啥也不做 等待创建事务的方法再决定是否需要回滚事务
			else if (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");
			}
		}
		catch (RuntimeException ex) {
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
			throw ex;
		}
		catch (Error err) {
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
			throw err;
		}
		// 循环遍历事务同步管理器的事务同步器
		// 主要是循环遍历事务同步管理器的synchronization.afterCompletion
		// 当前还得判断是当前方法里是开启了新事务
		// 并且清空事务同步器
		triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
	}
	finally {
		cleanupAfterCompletion(status);
	}
}

三、cleanupAfterCompletion

处理完回滚事务逻辑后(不一定会回滚,只有在开始事务的方法上才会真正回滚),持有的datasource的Connection设置还原且释放, 如果挂起了事务,需要恢复原有事务状态。

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
	//标记当前事务status对象已经完完成了,后续就不要来处理它了
	status.setCompleted();
	if (status.isNewSynchronization()) {
		// 如果是开始事务的status,则还原TransactionSynchronizationManager
		TransactionSynchronizationManager.clear();
	}
	if (status.isNewTransaction()) {
		// 如果是开始事务的status,则还原事务相关的的一些属性
		doCleanupAfterCompletion(status.getTransaction());
	}
	// 如果挂起了事务,则恢复原有的事务状态
	if (status.getSuspendedResources() != null) {
		if (status.isDebug()) {
			logger.debug("Resuming suspended transaction after completion of inner transaction");
		}
		resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
	}
}

doCleanupAfterCompletion

主要是还原datasource的connection的一些设置如autoCommit, 隔离级别,同时与当前线程解绑不在持有connection,并释放connection

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

	// Remove the connection holder from the thread, if exposed.
	if (txObject.isNewConnectionHolder()) {
		// 从线程变量中删除Connection,不再持有Connection
		TransactionSynchronizationManager.unbindResource(this.dataSource);
	}

	// Reset connection.
	Connection con = txObject.getConnectionHolder().getConnection();
	try {
		// 把Connection对象还原成AutoCommit
		if (txObject.isMustRestoreAutoCommit()) {
			con.setAutoCommit(true);
		}
		// 还原dataSource的隔离级别
		DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
	}
	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");
		}
		//释放Connection
		DataSourceUtils.releaseConnection(con, this.dataSource);
	}
	// 还原ConnectionHolder的属性值
	txObject.getConnectionHolder().clear();
}

resume

如果当前事务状态对象曾挂起过之前的事务对象,需要恢复之前的事务对象

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

	if (resourcesHolder != null) {
		Object suspendedResources = resourcesHolder.suspendedResources;
		if (suspendedResources != null) {
			// 将之前的connection对象重新绑定当前线程变量
			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);
			// 调用下这些事务同步器的resume方法,并把这些事务同步器重新放入事务同步管理器的线程变量中
			doResumeSynchronization(suspendedSynchronizations);
		}
	}
}

总结

在执行目标方法后遇到异常的处理流程:

  1. 根据目标方法的事务属性针对此异常是否需要回滚事务,还是提交事务
  2. 回滚事务的话,调用事务管理器PlatformTransactionManager的rollback进行回滚逻辑
  3. 回滚逻辑:
    - 如果是新事务,遍历调用事务同步器的beforeCompletion的方法
    - 如果当时事务是内嵌事务,则调用status的rollbackToHeldSavepoint方法,回滚savePoint
    - 如果当前事务状态transactionStatus对象是开启过事务的,则取出Connection调用它的rollback方法,进行真正的回滚事务的动作
    - 如果当前事务状态transactionStatus对象未开启过事务,但判断出它有事务,则标签当前线程上的事务需要回滚,交予开始事务的事务状态对象transactionStatus来进行回滚事务
    - 然后判断如果是开始事务的事务状态对象,则调用事务同步器的invokeAfterCompletion方法
    - 最后调用事务管理器的cleanupAfterCompletion方法,主要是判断如果当前开启过事务,然后就还原事务同步器,还原dataSource的Connection对象的一些属性,如隔离级别,autoCommit,然后从当前线程变量中释放掉Connection。如果当前事务状态对象还持有了挂起之前的事务资源对象,则进行之前的事务状态恢复。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值