个人理解的Spring 事务详解(终)

6 阅读14分钟

书接上回

通过上一篇以及之前的几篇,已经吧Spring Aop的识别,解析,加载,组装,以及bean的动态代理的过程都讲解了。所以本文会直接通过讲解spring事物的拦截器的执行逻辑讲解 @Transactional 执行过程

一般情况下,springboot已经默认使用CGlib的方式创建动态代理了,所以本文会切换到lCgib动态代理的执行过程,但是其实抛去使用的库的差异,CGlib和JDK创建代理以及代理逻辑执行其实没有太大的差别

CglibAopProxy 创建的代理,会在 org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept 方法中创建一个 CglibMethodInvocation (MethodInvocation类型) , 而它其实继承 ReflectiveMethodInvocation ,接着还是调用其proceed方法

TransactionInterceptor

这个就是Spring内置的事务拦截器的实现,实现了MethodInterceptor接口,那就直接看其 invoke 实现吧

把涉及到的两个方法拿出来,可以很明显的看到这个拦截器就是一个环绕逻辑,其主要也就做了下面几件事

1、解析方法上的事务注解,然后封装成 TransactionAttribute

2、获取事务管理器 TransactionManager

3、创建 TransactionInfo

4、执行 methodInvocation.proceed 返回控制权或者说执行真正的业务方法逻辑

5、cleanupTransactionInfo(txInfo); 清理TransactionInfo

6、commitTransactionAfterReturning(txInfo); 提交事务并执行一些callback逻辑

下面详细详解下以上6步

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
    @Override
    @Nullable
    public Object proceedWithInvocation() throws Throwable {
        return invocation.proceed();
    }
    @Override
    public Object getTarget() {
        return invocation.getThis();
    }
    @Override
    public Object[] getArguments() {
        return invocation.getArguments();
    }
});
}

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

    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    final TransactionManager tm = determineTransactionManager(txAttr);
    //.......省略......
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

        Object retVal;
        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);
        }

        if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
            // Set rollback-only in case of Vavr failure matching our rollback rules...
            TransactionStatus status = txInfo.getTransactionStatus();
            if (status != null && txAttr != null) {
                retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
            }
        }

        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    else {
        Object result;
        final ThrowableHolder throwableHolder = new ThrowableHolder();

        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
                try {
                    Object retVal = invocation.proceedWithInvocation();
                    if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
                        // Set rollback-only in case of Vavr failure matching our rollback rules...
                        retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
                    }
                    return retVal;
                }
                catch (Throwable ex) {
                    if (txAttr.rollbackOn(ex)) {
                        // A RuntimeException: will lead to a rollback.
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        }
                        else {
                            throw new ThrowableHolderException(ex);
                        }
                    }
                    else {
                        // A normal return value: will lead to a commit.
                        throwableHolder.throwable = ex;
                        return null;
                    }
                }
                finally {
                    cleanupTransactionInfo(txInfo);
                }
            });
        }
        catch (ThrowableHolderException ex) {
            throw ex.getCause();
        }
        catch (TransactionSystemException ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                ex2.initApplicationException(throwableHolder.throwable);
            }
            throw ex2;
        }
        catch (Throwable ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
            }
            throw ex2;
        }

        // Check result state: It might indicate a Throwable to rethrow.
        if (throwableHolder.throwable != null) {
            throw throwableHolder.throwable;
        }
        return result;
    }
}

1、解析TransactionAttribute

这个解析工作其实在前面介绍事务Advisor的时候有提到过正是利用了配置注入中set的TransactionAttributeSource进行解析的。下面在看下,正是通过SpringTransactionAnnotationParser 对方法上的 @Transactional 注解进行解析,然后创建了一个RuleBasedTransactionAttribute

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
		RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

		Propagation propagation = attributes.getEnum("propagation");
		rbta.setPropagationBehavior(propagation.value());
		Isolation isolation = attributes.getEnum("isolation");
		rbta.setIsolationLevel(isolation.value());

		rbta.setTimeout(attributes.getNumber("timeout").intValue());
		String timeoutString = attributes.getString("timeoutString");
		Assert.isTrue(!StringUtils.hasText(timeoutString) || rbta.getTimeout() < 0,
				"Specify 'timeout' or 'timeoutString', not both");
		rbta.setTimeoutString(timeoutString);

		rbta.setReadOnly(attributes.getBoolean("readOnly"));
		rbta.setQualifier(attributes.getString("value"));
		rbta.setLabels(Arrays.asList(attributes.getStringArray("label")));

		List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
		for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
			rollbackRules.add(new RollbackRuleAttribute(rbRule));
		}
		for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
			rollbackRules.add(new RollbackRuleAttribute(rbRule));
		}
		for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
			rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
		}
		for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
			rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
		}
		rbta.setRollbackRules(rollbackRules);

		return rbta;
	}

2、获取事务管理器

获取事务管理器是通过

final TransactionManager tm = determineTransactionManager(txAttr); 这里执行的,其实最终还是通过 Spring的Bean容器获取TransactionManager类型的Bean

defaultTransactionManager = this.beanFactory.getBean(TransactionManager.class);

3、创建TransactionInfo

TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification) 这一行代码真是内有千秋,有点复杂,也非常重要,做了很多事情,为了好理解,这里以第一次进入事务的场景为例进行讲解

可以看到逻辑被分成了两大步,

1、根据事务属性TransactionAttribute获取一个TransactionStatus

2、根据事务管理器,事务属性,前一步生成的事务状态,以及事物名称,生成一个TransactionInfo

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// If no name specified, apply method identification as transaction name.
		if (txAttr != null && txAttr.getName() == null) {
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				status = tm.getTransaction(txAttr);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
							"] because no transaction manager has been configured");
				}
			}
		}
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

这里有必要简单解释下这些实体的含义,

tm很好理解,他就是管理器,一个中心调度器,用于管理事务的,包括事务当前运行状态,事务提交与回滚的执行等

TransactionAttribute 本质是一个 TransactionDefinition ,就是事务的元信息

TransactionStatus 事务状态呢,相对TransactionDefinition表达的是一种动态的,运行期的对象,描述了当前事务的一些状态,包括会组合当前事务的一些资源信息(比如db的connection),以及还会存储挂起的资源信息(比如前一个被挂起的事务的db的connection)

TransactionInfo 这个嘛,既然已经有了TransactionStatus了,似乎已经够了吧,这个TransactionInfo 又是什么,为什么还要抽象一个它出来呢?那就要看下这个TransactionInfo的定义了。可以看到其实它是一个更大的容器,组合了tm,ta,ts,以及一个很重要的oldTransactionInfo。所以它和TransactionStatus还是有很大差异的,TransactionInfo记录一个事务所有的环境信息

protected static final class TransactionInfo {

		@Nullable
		private final PlatformTransactionManager transactionManager;

		@Nullable
		private final TransactionAttribute transactionAttribute;

		private final String joinpointIdentification;

		@Nullable
		private TransactionStatus transactionStatus;

		@Nullable
		private TransactionInfo oldTransactionInfo;

		public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,
				@Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {

			this.transactionManager = transactionManager;
			this.transactionAttribute = transactionAttribute;
			this.joinpointIdentification = joinpointIdentification;
		}
	}

解释完这些领域实体的概念后,拆解上面两步,

status = tm.getTransaction(txAttr)

可以看到其实逻辑还是比较清晰的,先获取一个transaction对象() , 之后判断当前是否存在事务,如果存在,则通过handleExistingTransaction来处理(后面再讲解); 如果不存在(一般第一次执行事务都是走这条分支),则再根据事务传播属性分别走不同的逻辑,那一般情况,我们的事务方法都是使用默认的传播类型也就是required。所以会走到else if分支中

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException {

		// Use defaults if no transaction definition given.
		TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

		Object transaction = doGetTransaction();
		boolean debugEnabled = logger.isDebugEnabled();

		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(def, transaction, debugEnabled);
		}

		// Check definition settings for new transaction.
		if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
		}

		// No existing transaction found -> check propagation behavior to find out how to proceed.
		if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
			throw new IllegalTransactionStateException(
					"No existing transaction found for transaction marked with propagation 'mandatory'");
		}
		else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + def);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
		}
	}

先看获取transaction 对象的过程

这里就是创建一个DataSourceTransactionObject对象,然后通过ThreadLocal的方式获取key=datasource的value ,也就是ConnectionHolder ,此时由于是第一次,所以这里是null

@Override
	protected Object doGetTransaction() {
		DataSourceTransactionObject txObject = new DataSourceTransactionObject();
		txObject.setSavepointAllowed(isNestedTransactionAllowed());
		ConnectionHolder conHolder =
				(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
		txObject.setConnectionHolder(conHolder, false);
		return txObject;
	}

之后

else if(def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ) 逻辑

可以看到,其实逻辑也很简单,因为走到这里已经确定此时上下文中是么有事务的,所以这里直接挂起一个null,然后得到的挂起的资源,那肯定也是null了。之后就是开启一个新事务,然后封装成一个 TransactionStatus,并返回了

else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
	// 由于这里是没有事务,所有会挂起挂起一个null,背后什么都没做,并且返回一个null	
    SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
			}
			try {
                //然后会开启一个新的事务并封装成一个TransactionStatus返回
				return startTransaction(def, transaction, debugEnabled, suspendedResources);
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}

suspend(null)

这里如上所说,这里什么都不做,返回null

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
			try {
				Object suspendedResources = null;
				if (transaction != null) {
					suspendedResources = doSuspend(transaction);
				}
				String name = TransactionSynchronizationManager.getCurrentTransactionName();
				TransactionSynchronizationManager.setCurrentTransactionName(null);
				boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
				TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
				Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
				boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
				TransactionSynchronizationManager.setActualTransactionActive(false);
				return new SuspendedResourcesHolder(
						suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
			}
			catch (RuntimeException | Error ex) {
				// doSuspend failed - original transaction is still active...
				doResumeSynchronization(suspendedSynchronizations);
				throw ex;
			}
		}
		else if (transaction != null) {
			// Transaction active but no synchronization active.
			Object suspendedResources = doSuspend(transaction);
			return new SuspendedResourcesHolder(suspendedResources);
		}
		else {
			// Neither transaction nor synchronization active.
			return null;
		}
	}

startTransaction(def, transaction, debugEnabled, suspendedResources)

这个是核心方法,做了三件事

1、创建一个DefaultTransactionStatus

2、开启一个事务

3、为DefaultTransactionStatus 或者是事务上下文准备同步器

分别来分析

private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
			boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {

		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
		doBegin(transaction, definition);
		prepareSynchronization(status, definition);
		return status;
	}

1、newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources) 这里很简单,就是根据传入的参数,通过构造器构造一个DefaultTransactionStatus 对象

2、doBegin(transaction, definition)

这个方法其实主要就做了两件事,查单当前 DataSourceTransactionObject中是否持有ConnectionHolder, 由上面可知,支持创建了 DataSourceTransactionObject 对象,并且由于是第一次进来,所以这里DataSourceTransactionObject并没有持有ConnectionHolder。所以这里会从datasource中获取connection,然后把connection生成一个ConnectionHolder并设置到DataSourceTransactionObject 中。

之后就是设置DataSourceTransactionObject的一些属性了,比较重要的一步是con.setAutoCommit(false)

最后一步很重要就是 TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); 把<datasource,connectionholder> 绑定到ThreadLocal中,之后就可以供诸如mybatis等框架可以从ThreadLocal中使用同一个datasource进行获取connection了

之前问题解答

所以从这里就可以解答之前提出的问题,spring在事务一开始的时候就创建了db connection, 而不管你后续有没有真正的db操作,或者如果遇到if提前返回的情况

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    Connection con = null;

    try {
        if (!txObject.hasConnectionHolder() ||
                txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            Connection newCon = obtainDataSource().getConnection();
            if (logger.isDebugEnabled()) {
                logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
            }
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }

        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();

        Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);
        txObject.setReadOnly(definition.isReadOnly());

        // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
        // so we don't want to do it unnecessarily (for example if we've explicitly
        // configured the connection pool to set it already).
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            if (logger.isDebugEnabled()) {
                logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
            }
            con.setAutoCommit(false);
        }

        prepareTransactionalConnection(con, definition);
        txObject.getConnectionHolder().setTransactionActive(true);

        int timeout = determineTimeout(definition);
        if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
            txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
        }

        // Bind the connection holder to the thread.
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
        }
    }

    catch (Throwable ex) {
        if (txObject.isNewConnectionHolder()) {
            DataSourceUtils.releaseConnection(con, obtainDataSource());
            txObject.setConnectionHolder(null, false);
        }
        throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
    }
}

3、prepareSynchronization

这个方法就是设置各种状态以及对象到ThreadLocal中,以及初始化事务同步器集合为一个空集合,供后续的使用

protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
        TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
                definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
                        definition.getIsolationLevel() : null);
        TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
        TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
        TransactionSynchronizationManager.initSynchronization();
    }
}

完成上面三步之后,就返回了一个装配好的 TransactionStatus ,可以梳理下现在这个TransactionStatus中都有组合了哪些东西

包括:

DataSourceTransactionObject (里面持有一个ConnectionHolder)

newTransaction = true (表示主事务)

newSynchronization = true (表示已经一个新的事务同步,由这个事务来控制 Synchronization的各种回调处理)

completed = false (事务尚未完成)

suspendedResources = null (没有挂起的资源)

接着

prepareTransactionInfo(tm, txAttr, joinpointIdentification, status)

这一步,主要是通过 上面创建好的TransactionStatus 创建一个 TransactionInfo

可以看到就是先通过传入的参数构建好一个TransactionInfo ,之后再把TransactionStatus 设置进去,最后把 TransactionInfo绑定到当前线程

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, String joinpointIdentification,
			@Nullable TransactionStatus status) {

		TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
		if (txAttr != null) {
			//.........省略一些代码.........
            txInfo.newTransactionStatus(status);
		}
		else {
					if (logger.isTraceEnabled()) {
				logger.trace("No need to create transaction for [" + joinpointIdentification +
						"]: This method is not transactional.");
			}
		}
		txInfo.bindToThread();
		return txInfo;
	}
private void bindToThread() {
			// Expose current TransactionStatus, preserving any existing TransactionStatus
			// for restoration after this transaction is complete.
			this.oldTransactionInfo = transactionInfoHolder.get();
			transactionInfoHolder.set(this);
}

4、执行 retVal = invocation.proceedWithInvocation()

这一步没什么说的,就是执行业务方法

5、cleanupTransactionInfo(txInfo);

还原ThreadLocal

protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
		if (txInfo != null) {
			txInfo.restoreThreadLocalStatus();
		}
}

private void restoreThreadLocalStatus() {
			// Use stack to restore old transaction TransactionInfo.
			// Will be null if none was set.
			transactionInfoHolder.set(this.oldTransactionInfo);
		}

6、commitTransactionAfterReturning(txInfo);

这个就是对事务进行提交以及执行callback逻辑了,方法最终会执行到这里,可以看到又是执行了很多小的步骤。其中prepareForCommit是空实现。

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");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					status.releaseHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction commit");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					doCommit(status);
				}
				else if (isFailEarlyOnGlobalRollbackOnly()) {
					unexpectedRollback = status.isGlobalRollbackOnly();
				}

				// Throw UnexpectedRollbackException if we have a global rollback-only
				// marker but still didn't get a corresponding exception from commit.
				if (unexpectedRollback) {
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			catch (UnexpectedRollbackException ex) {
				// can only be caused by doCommit
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
				throw ex;
			}
			catch (TransactionException ex) {
				// can only be caused by doCommit
				if (isRollbackOnCommitFailure()) {
					doRollbackOnCommitException(status, ex);
				}
				else {
					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				}
				throw ex;
			}
			catch (RuntimeException | Error ex) {
				if (!beforeCompletionInvoked) {
					triggerBeforeCompletion(status);
				}
				doRollbackOnCommitException(status, ex);
				throw ex;
			}

			// Trigger afterCommit callbacks, with an exception thrown there
			// propagated to callers but the transaction still considered as committed.
			try {
				triggerAfterCommit(status);
			}
			finally {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}

		}
		finally {
			cleanupAfterCompletion(status);
		}
	}

看下 triggerBeforeCommit(status) 内部实现

可以看到就是获取到所有的Synchronization,然后逐个执行其beforeCommit方法,这就是Spring的事务钩子方法,其中在之前分析mybatis-spring 的时候,其中就在SqlSessionTemplate的代理中获取SqlSession的时候有注册一个SqlSessionSynchronization, 这里就会回调到SqlSessionSynchronization的beforeCommit方法

protected final void triggerBeforeCommit(DefaultTransactionStatus status) {
		if (status.isNewSynchronization()) {
			TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
		}
}


public static void triggerBeforeCommit(boolean readOnly) {
		for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
			synchronization.beforeCommit(readOnly);
		}
}

SqlSessionSynchronization的beforeCommit方法 ,就是调用了sqlSession的commit方法

@Override
    public void beforeCommit(boolean readOnly) {
      // Connection commit or rollback will be handled by ConnectionSynchronization or
      // DataSourceTransactionManager.
      // But, do cleanup the SqlSession / Executor, including flushing BATCH statements so
      // they are actually executed.
      // SpringManagedTransaction will no-op the commit over the jdbc connection
      // TODO This updates 2nd level caches but the tx may be rolledback later on!
      if (TransactionSynchronizationManager.isActualTransactionActive()) {
        try {
          LOGGER.debug(() -> "Transaction synchronization committing SqlSession [" + this.holder.getSqlSession() + "]");
          this.holder.getSqlSession().commit();
        } catch (PersistenceException p) {
          if (this.holder.getPersistenceExceptionTranslator() != null) {
            DataAccessException translated = this.holder.getPersistenceExceptionTranslator()
                .translateExceptionIfPossible(p);
            if (translated != null) {
              throw translated;
            }
          }
          throw p;
        }
      }
    }

其他的钩子方法不在一一分析了,基本都是相同的套路

接着就是

doCommit了,这里就是通过Connection做真正的commit

再Commit完成之后,又做了两次callback: triggerAfterCommit 和 triggerAfterCompletion

其中triggerAfterCompletion 比较重要一些,看下其中的逻辑。

主要就是如果当前的事务如果是主事务,那么就清空ThreadLocal中所有的Synchronizations, 然后callback Synchronization的afterCompletion 方法

private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
    if (status.isNewSynchronization()) {
        List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
        TransactionSynchronizationManager.clearSynchronization();
        if (!status.hasTransaction() || status.isNewTransaction()) {
            // No transaction or new transaction for the current scope ->
            // invoke the afterCompletion callbacks immediately
            invokeAfterCompletion(synchronizations, completionStatus);
        }
        else if (!synchronizations.isEmpty()) {
            // Existing transaction that we participate in, controlled outside
            // the scope of this Spring transaction manager -> try to register
            // an afterCompletion callback with the existing (JTA) transaction.
            registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
        }
    }
}

最后

cleanupAfterCompletion(status);

可以看到把 TransactionStatus 设置为完成,主要三件事

1、清理各种ThreadLocal

2、在事务完成之后,将TransactionStatus持有的DataSourceTransactionObject 也就是Connection返回给Datasource, 释放连接

3、恢复之前的事务上下文

下面分别拆解

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
		status.setCompleted();
		if (status.isNewSynchronization()) {
			TransactionSynchronizationManager.clear();
		}
		if (status.isNewTransaction()) {
			doCleanupAfterCompletion(status.getTransaction());
		}
		if (status.getSuspendedResources() != null) {
			if (status.isDebug()) {
				logger.debug("Resuming suspended transaction after completion of inner transaction");
			}
			Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
			resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
		}
	}

1、清理各种ThreadLocal

public static void clear() {
		synchronizations.remove();
		currentTransactionName.remove();
		currentTransactionReadOnly.remove();
		currentTransactionIsolationLevel.remove();
		actualTransactionActive.remove();
}

2、在事务完成之后,将TransactionStatus持有的DataSourceTransactionObject 也就是Connection返回给Datasource, 释放连接

可以看到基本即使解绑Datasource和ConnectionHolder ,恢复Connection的各种默认属性,并将Connection返回回链接池,之后清理ConnectionHolder

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();
	}

3、恢复之前的事务上下文

这个当第一次时,不会执行,当在一个已经存在事务上下文中时,其实做的也很清晰,就是把之前存储的资源重新set回去

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);
        }
    }
}

7、当事务存在,且第二个事务是Required_New类型时

对于这种场景,其实主要的处理步骤就是之前说的在创建TransactionStatus时,已经存在事务时,走 handleExistingTransaction(def, transaction, debugEnabled) 的逻辑了

这个方法里其实做的事情也很清晰,还是先获取下当前线程中的

DataSourceTransactionObject ,这时候肯定是有值的

之后的逻辑处理大同小异,也是根据新的方法中事务传播属性来创建TransactionStatus ,比如新进的方法的事务传播类型是Required_New,那么肯定之前获取的DataSourceTransactionObject 肯定是要作为被挂起的资源对象被TransactionStatus 持有,而新创建的TransactionStatus肯定是要重新获取一个Connection. 之后创建TransactionInfo ,同样,TransactionInfo中也会组合一个之前的oldTransactionInfo .

在新方法执行完成之后,原先持有的前一个方法的资源,都要被重新设置回去,然后继续走上一个方法事务拦截器的后置逻辑,也就是上一篇说的,先进来的interceptor的后置逻辑会后执行

总结

本篇算是完成了对spring事务拦截器主要逻辑的梳理,同时回答了之前提出的问题。

spring在事务一开始就创建了db的connection,而不管业务方法中是否真正有使用connection。当然spring其实也知道这事,当然也有对应的解决方案。不过,一般情况下,也不会出现业务方法都没操作db,方法确被添加了事务注解吧。但是可能出现,if提前返回,执行不到db的情况。此时创建的connection或者从连接池中获取的connection其实并没有被真正使用。

总的来说,由于spring考虑的非常全面而且覆盖的场景非常多,所以事务拦截器的实现逻辑还是比较复杂的,其中有很多组件的参与,以及其中有几个组件有大量的状态字段,spring通过这些状态控制了很多逻辑,导致整体逻辑如果想真正掌握还是有些难度的,但是如果只是理解整体的链路的话,相对来说不是很难