Spring事务的7大传播特性是如何实现的

358 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情

前言

java程序员都基本上会使用spring,在对数据库进行操作时,常常涉及到事务的处理,spring事务的实现是基于数据库本身的事务,但是spring事务在数据库事务的基础上做了一定的增强,比如7大传播特性的实现可以帮助开发人员在复杂的业务需求中灵活地应对,那么这7大传播特性是如何实现的呢?

源码解析

关于上面的问题,我们可以定位到AbstractPlatformTransactionManager.getTransaction()方法:

   public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
       // 获取事务定义,其实就是我们写代码的时候加上的@Transactional解析得到的TransactionDefinition
       TransactionDefinition def = definition != null ? definition : TransactionDefinition.withDefaults();
       // 获取当前的事务,其实就是从ThreadLocal中获取当前线程是否正在其他事务当中
       Object transaction = this.doGetTransaction();
       boolean debugEnabled = this.logger.isDebugEnabled();
       // 如果当前存在事务
       if (this.isExistingTransaction(transaction)) {
           // 根据传播特性来处理,后面会讲解;handleExistingTransaction方法处理的都是在已经存在事务的情况下的业务逻辑
           return this.handleExistingTransaction(def, transaction, debugEnabled);
      } else if (def.getTimeout() < -1) {
           throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
      } else if (def.getPropagationBehavior() == 2) {
           // 如果传播特性设置的是PROPAGATION_MANDATORY,2代表PROPAGATION_MANDATORY,如果当前没有事务,那么抛异常
           throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
      } else if (def.getPropagationBehavior() != 0 && def.getPropagationBehavior() != 3 && def.getPropagationBehavior() != 6) {
         // 如果传播特性不为0,3,6;其实就表示如果传播特性为1或4或5;也就是PROPAGATION_SUPPORTS或PROPAGATION_NOT_SUPPORTED或PROPAGATION_NEVER,以非事务状态执行
           if (def.getIsolationLevel() != -1 && this.logger.isWarnEnabled()) {
               this.logger.warn("Custom isolation level specified but no actual transaction initiated; isolation level will effectively be ignored: "+ def);
          }
​
           boolean newSynchronization = this.getTransactionSynchronization() == 0;
           return this.prepareTransactionStatus(def, (Object)nulltrue, newSynchronization, debugEnabled, (Object)null);
      } else {
           // 剩下的情况就是传播特性为0或3或6,也就是PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED,那么就开启新事务执行
           AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = this.suspend((Object)null);
           if (debugEnabled) {
               this.logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
          }
​
           try {
               return this.startTransaction(def, transaction, debugEnabled, suspendedResources);
          } catch (Error | RuntimeException var7) {
               this.resume((Object)null, suspendedResources);
               throw var7;
          }
      }
  }

在上述源码中,我们主要关注如果当前不存在事务的前提下,spring是如何处理的:

1.当前存在事务,交给handleExistingTransaction()处理,后续讲解;

下面都是当前没有事务的情况下:

2.在没有事务的情况下,如果设置的传播特性是PROPAGATION_MANDATORY,那么抛异常;所以PROPAGATION_MANDATORY是要求当前必须存在事务的;

3.在没有事务的情况下,如果设置的传播特性是[PROPAGATION_SUPPORTS,PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER]之一,那么都以非事务状态执行;

4.在没有事务的情况下,如果设置的传播特性是[PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED]之一,那么将开启新事务执行;

下面我们来看一下handleExistingTransaction()是如何处理有事务的情况的:

   private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled) throws TransactionException {
       // 在有事务的情况下,如果当前传播特性设置的是PROPAGATION_NEVER,那么直接抛异常;
       if (definition.getPropagationBehavior() == 5) {
           throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");
      } else {
           AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources;
           // 在有事务的情况下,如果传播特性是PROPAGATION_NOT_SUPPORTED,那么以非事务状态执行;
           if (definition.getPropagationBehavior() == 4) {
               if (debugEnabled) {
                   this.logger.debug("Suspending current transaction");
              }
​
               suspendedResources = this.suspend(transaction);
               boolean newSynchronization = this.getTransactionSynchronization() == 0;
               return this.prepareTransactionStatus(definition, (Object)nullfalse, newSynchronization, debugEnabled, suspendedResources);
          } else if (definition.getPropagationBehavior() == 3) {
               // 在有事务的情况下,如果传播特性是PROPAGATION_REQUIRES_NEW,那么将开启新事务执行;
               if (debugEnabled) {
                   this.logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]");
              }
​
               suspendedResources = this.suspend(transaction);
​
               try {
                   return this.startTransaction(definition, transaction, debugEnabled, suspendedResources);
              } catch (Error | RuntimeException var6) {
                   this.resumeAfterBeginException(transaction, suspendedResources, var6);
                   throw var6;
              }
          } else if (definition.getPropagationBehavior() == 6) {
               // 在有事务的情况下,如果传播特性是PROPAGATION_NESTED并且允许嵌套事务的情况下,将开启新事务执行,否则将抛异常
               if (!this.isNestedTransactionAllowed()) {
                   throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - specify 'nestedTransactionAllowed' property with value 'true'");
              } else {
                   if (debugEnabled) {
                       this.logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
                  }
​
                   if (this.useSavepointForNestedTransaction()) {
                       DefaultTransactionStatus status = this.prepareTransactionStatus(definition, transaction, falsefalse, debugEnabled, (Object)null);
                       status.createAndHoldSavepoint();
                       return status;
                  } else {
                       return this.startTransaction(definition, transaction, debugEnabled, (AbstractPlatformTransactionManager.SuspendedResourcesHolder)null);
                  }
              }
          } else {
               // 在有事务的情况下,如果传播特性为PROPAGATION_REQUIRED或PROPAGATION_SUPPORTS或PROPAGATION_MANDATORY,那么直接使用当前事务执行;
               if (debugEnabled) {
                   this.logger.debug("Participating in existing transaction");
              }
​
               if (this.isValidateExistingTransaction()) {
                   if (definition.getIsolationLevel() != -1) {
                       Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
                       if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
                           Constants isoConstants = DefaultTransactionDefinition.constants;
                           throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] specifies isolation level which is incompatible with existing transaction: " + (currentIsolationLevel != null ?isoConstants.toCode(currentIsolationLevel, "ISOLATION_") : "(unknown)"));
                      }
                  }
​
                   if (!definition.isReadOnly() && TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                       throw new IllegalTransactionStateException("Participating transaction with definition [" + definition + "] is not marked as read-only but existing transaction is");
                  }
              }
​
               boolean newSynchronization = this.getTransactionSynchronization() != 2;
               return this.prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, (Object)null);
          }
      }
  }

通过上述源码分析,我们很清楚地知道了在有事务的情况下,事务的传播特性是如何处理的:

1.在有事务的情况下,如果当前传播特性设置的是PROPAGATION_NEVER,那么直接抛异常;

2.在有事务的情况下,如果传播特性是PROPAGATION_NOT_SUPPORTED,那么挂起当前事务,并以非事务状态执行业务逻辑;

3.在有事务的情况下,如果传播特性是PROPAGATION_REQUIRES_NEW,那么挂起当前事务,并将开启新事务执行;

4.在有事务的情况下,如果传播特性是PROPAGATION_NESTED并且允许嵌套事务的情况下,将开启新事务执行,否则将抛异常

5.在有事务的情况下,如果传播特性为PROPAGATION_REQUIRED或PROPAGATION_SUPPORTS或PROPAGATION_MANDATORY,那么直接使用当前事务执行;