很抱歉,Spring事务本来应该在上一篇就结束的,但因为梳理过程中发现了更多的未知知识,所以便再啰嗦几篇。本篇主要针对前一篇文章——《Spring 事务原理总结四》——末尾提到的几个问题进行梳理,这里再回顾一下这几个问题:
通过前一篇文章,我们可以很确定Spring事务是通过代理方式实现的,其最终的处理类为TransactionInterceptor,而这个类中的invoke(MethodInvocation)方法是执行事务的起点,这个方法又继续调用了本类(TransactionInterceptor)的父类——TransactionAspectSupport——中的invokeWithinTransaction(Method, Class>, InvocationCallback)方法,这个方法是实现事务控制逻辑的核心,继续跟踪会看到上节提到的创建TransactionInfo对象的代码,即:
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
之后我们继续进入该方法(其位于TransactionAspectSupport类中),这个方法内部有一段创建TransactionStatus对象的逻辑:
- 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");
- }
- }
- }
从此处,我们进入到AbstractPlatformTransactionManager#getTransaction(TransactionDefinition)方法内部(注意:AbstractPlatformTransactionManager抽象类实现了PlatformTransactionManager接口,如果想了解其继承结构,可以看一下《Spring 事务原理总结三》这篇文章;还有上篇博客《Spring 事务原理总结四》中说这个方法接收的参数的类型为TransactionAttribute,这里想纠正一下,实际类型为TransactionDefinition),这里我们再贴一下该方法的源码:
- @Override
- 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, false, 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);
- }
- }
下面让我们一起来梳理一下这个方法的具体处理逻辑吧!首先让我们一起来看一下下面这幅图:

从图中不难发现,这个方法会首先获得一个TransactionDefinition对象(如果传递进来的对象为空则会返回一个StaticTransactionDefinition类型的TransactionDefinition对象),接下来会调用DataSourceTransactionManager#doGetTransaction(),创建一个DataSourceTransactionObject类型的对象(该类是DataSourceTransactionManager中定义的一个静态内部类),关于该类的继承结构如下图所示:

下面看一下doGetTransaction()方法的源码,具体如下所示:
- protected Object doGetTransaction() {
- DataSourceTransactionObject txObject = new DataSourceTransactionObject();
- txObject.setSavepointAllowed(isNestedTransactionAllowed());
- ConnectionHolder conHolder =
- (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
- txObject.setConnectionHolder(conHolder, false);
- return txObject;
- }
下面再来看一下ConnectionHolder这个类,它位于org.springframework.jdbc.datasource中,其继承结构如下所示:

总之,结合doGetTransaction()方法源码,我们可以清楚看到,其主要作用就是创建一个DataSourceTransactionObject对象。回到AbstractPlatformTransactionManager中的getTransaction()方法中,继续向下走,见下图:

下面让我们一起看一下DataSourceTransactionManager类中的isExistingTransaction()方法的源码,具体如下所示:
- @Override
- protected boolean isExistingTransaction(Object transaction) {
- DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
- return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
- }
这个方法的主要作用就是判断当前是否存在存活的事务,如果存在事务,则处理之,否则继续。接着就是判断事务是否超时,如果超时,直接抛出超时异常,否则继续。之后就是对事务传播行为的判断,首先就是判断当前的事务传播行为是否为PROPAGATION_MANDATORY,根据《Spring 事务原理总结一》这篇文章的介绍,这个事务传播属性的作用是如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。所以这里有这样一段代码:
- if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
- throw new IllegalTransactionStateException(
- "No existing transaction found for transaction marked with propagation 'mandatory'");
- }
接着判断当前的事务传播行为是否是PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED,根据《Spring 事务原理总结一》这篇文章的介绍,它们三个的作用分别为:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务;创建一个新的事务,如果当前存在事务,则把当前事务挂起;如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED。这个判断逻辑的详细代码如下所示:
- 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, false, debugEnabled, suspendedResources);
- }
- catch (RuntimeException | Error ex) {
- resume(null, suspendedResources);
- throw ex;
- }
- }
由于我们使用的是默认配置,即PROPAGATION_REQUIRED,所以我们跟踪的案例会走到这段分支。这个分支中的suspend(null)表示暂停某某某(关于这部分暂时不做解释)。先来看下面这幅图:

接着看一下startTransaction(TransactionDefinition definition, Object transaction, boolean nested, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources)这个方法(注意这个方法位于AbstractPlatformTransactionManager类中)的源码(根据该方法的方法名可知该方法的主要作用是开始一个新事务,这是个人理解,后续会根据理解的深入而进行变更),具体如下所示:
- private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
- boolean nested, boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
-
- boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
- DefaultTransactionStatus status = newTransactionStatus(
- definition, transaction, true, newSynchronization, nested, debugEnabled, suspendedResources);
- this.transactionExecutionListeners.forEach(listener -> listener.beforeBegin(status));
- try {
- doBegin(transaction, definition);
- }
- catch (RuntimeException | Error ex) {
- this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, ex));
- throw ex;
- }
- prepareSynchronization(status, definition);
- this.transactionExecutionListeners.forEach(listener -> listener.afterBegin(status, null));
- return status;
- }
这段逻辑中我们主要看一下DataSourceTransactionManager#doBegin(Object transaction, TransactionDefinition definition)方法,其源码如下所示:
- 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);
- }
- }
注意这段逻辑中有这样一句代码:con.setAutoCommit(false)。这就解释了上篇文章末尾和本篇文章开头提到的问题:为什么这里没看到conn.setAutoCommit(false)。这个逻辑写在了DataSourceTransactionManager类的doBegin()方法中。关于这个方法中的其他细节,这里不过多解释,后期如果理解透彻了,我会继续添加。最后让我们一起回到AbstractPlatformTransactionManager的getTransaction(@Nullable TransactionDefinition definition)方法中,然后继续返回一直返回到最初的调用者,即TransactionAspectSupport的createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification)方法中,然后继续向上返回,直到TransactionAspectSupport类的invokeWithinTransaction(Method method, @Nullable Class> targetClass, final InvocationCallback invocation)方法中。这样我们就创建了一个TransactionInfo对象,然后就可以按照前面一章——《Spring 事务原理总结四》——描述的流程继续向下走了。
至此我们解决了昨天遗留问题中的两个,即本篇篇首所列的问题一和问题二。那其他两个问题呢?首先第三个问题个人觉得是流程梳理性质的问题,所以本篇不再过分着墨,我会在新篇章中对这个问题进行详尽的梳理。接着第四个问题属于面试性质的考题,可以在本篇文章中进行梳理。因此接下来我将对第四个问题进行梳理,如果有不对的地方,还请大家多多指教,谢谢!常见的造成事务失效的场景有以下几种:
好了,今天就这样吧,虽然没能兑现自己的诺言,但总算弄懂了几个问题(剩余的那个问题我将在下一篇博文中重点介绍)。回头看看那遍布荆棘的来路,不知道自己是怎么一步一步走来的,抬头望着前方那泥泞不堪的陡峭绝路,我的内心毫无波澜,不是什么都不会导致的麻木不仁,更不是破罐子破摔的自弃心理作祟,只是我知道急躁会使事情更加糟糕。所以与其手忙脚乱,不如一步一个脚印的慢慢前行。