Spring事务源码(四)
本文主要讲述Spring事务在获得事务对象后,再执行目标对象的原生方法后没有遇到异常,如何处理事务。通常是通过commitTransactionAfterReturning这个方法解决。执行完此方法,那么Spring事务也就结束了。
文章目录
前言
我们需要回到TransactionAspectSupport#invokeWithinTransaction这个方法来,前两篇文章都是讲诉如何获得TransactionStatus,最后封装成TransactionInfo。 而本文是讲述获得事务后,执行完目标方法后没有遇到异常后该怎么处理。 这里主要解析commitTransactionAfterReturning
```java
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;
}
}
一、commitTransactionAfterReturning
主要是判断下当前事务对象txInfo是有事务,如果有则调用事务管理器的commit方法
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
二、事务管理器的commit方法
@Override
public final void commit(TransactionStatus status) throws TransactionException {
// 如果当前事务状态对象status被标识已完成,则抛出异常
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;
// 如果对象被标记了回滚,那么调用回滚事务的方法,这个一般不太可能发生
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
return;
}
// 这里有可能在同一线程内被其他带有事务的方法抛出异常,把当前事务标记需要回滚
// 那么执行回滚事务动作,并抛出一个异常
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}
// 这里开始处理提交事务的逻辑
processCommit(defStatus);
}
processCommit
真正处理当前线程的事务提交
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
// 目前无任何逻辑
prepareForCommit(status);
// 如果status是开始了事务的,那么出发事务同步器的synchronization.beforeCommit方法
triggerBeforeCommit(status);
// 如果status是开始了事务的,那么出发事务同步器的synchronization.beforeCompletion方法
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
// 如果事务状态status对象持有savePoint,说明目标方法开启的内嵌事务,即传播行为是Nested
// 提交savePoint
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
// 如果事务状态对象是真正持有事务的,那么就取出事务对象transactionObject的ConnectionHolder
// 然后取出Connection,调用Connection的commit方法提交事务
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
// 如果status是开始了事务的,那么出发事务同步器的synchronization.afterCommit方法
triggerAfterCommit(status);
}
finally {
// 如果status是开始了事务的,那么出发事务同步器的synchronization.afterCompletion方法
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
// 提交完事务,有些善后工作需要处理
cleanupAfterCompletion(status);
}
}
cleanupAfterCompletion
提交完事务,有些善后工作需要处理,比如释放Connection, 如果传播行为是Require_New,那还挂起了事务,需要将挂起的事务恢复
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 标记当时事务状态对象已完成了
status.setCompleted();
if (status.isNewSynchronization()) {
// 还原事务同步管理器
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());
}
}
总结
在执行目标方法后没有遇到异常的处理流程:
提交事务的话,调用事务管理器PlatformTransactionManager的commit进行回滚逻辑。
在此方法内判断下可能在同一线程内被其他带有事务的方法抛出异常,把当前事务标记需要回滚,那么需要执行回滚事务,然后抛出一个异常。如果没有这调用processCommit来真正提交事务
processCommit提交事务逻辑:
- 如果是新事务,遍历调用事务同步器的beforeCommit的方法
- 如果是新事务,遍历调用事务同步器的beforeCompletion的方法
- 如果事务状态status对象持有savePoint,说明目标方法开启的内嵌事务,即传播行为是Nested,那么提交savePoint
- 如果当前事务状态transactionStatus对象是开启过事务的,则取出Connection调用它的commit方法,进行真正的提交事务的动作
- 事务提交完毕后判断如果是开始事务的事务状态对象,则调用事务同步器的AfterCommit方法,然后再调用事他的cleanupAfterCompletion方法
- 最后判断如果当前开启过事务,然后就还原事务同步器,还原dataSource的Connection对象的一些属性,如隔离级别,autoCommit,然后从当前线程变量中释放掉Connection。如果当前事务状态对象还持有了挂起之前的事务资源对象,则进行之前的事务状态恢复。