spring原始碼解析 (七) 事務底層原始碼實現

乾了這杯檸檬多發表於2020-12-03

1.spring事務的本質

資料庫中的事務預設存在,只不過每次都自動提交了,資料庫中這個引數--autoCommitd=true

其本質是 begin sql commit

而spring事務其本質是資料庫一致,也是begin sql commit ,只不過將autoCommitd設定為了false。

而每個事務其實都是一個資料庫連線,根據不同的事務傳播機制進行掛起,儲存點等操作。

spring事務執行還是通過代理織入的

一個小demo:如果物件直接呼叫自己的方法,那麼這個@Transactional事務就不會觸發,因為事務執行的本質還是代理,直接呼叫就是原始物件方法的呼叫,沒有進行事務的代理邏輯,但如果自己注入自己,換成txService.a(),事務代理邏輯就會觸發,因為txService屬於代理物件,進行了代理邏輯的織入。

@Component
public class TxService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private TxService txService;

    public void insert11(){
        System.out.println("insert11");
       // txService.a();
        a();
    }
    @Transactional
    public void  a(){
        System.out.println("A");
        jdbcTemplate.execute("insert grade values(13,2,2,2)");
        int i =100/0;
    }
}

2.事務底層原始碼分析

首先引入了@EnableTransactionManagement

裡面註冊了兩個bean,AutoProxyRegistrar和ProxyTransactionManagementConfiguration

AutoProxyRegistrar主要作用是開啟自動代理

ProxyTransactionManagementConfiguration中定義了三個bean:

  1. BeanFactoryTransactionAttributeSourceAdvisor:一個PointcutAdvisor
  2. AnnotationTransactionAttributeSource:就是Pointcut
  3. TransactionInterceptor:就是代理邏輯Advice

(TransactionAttributeSource其實就是代表一個@Transactional)

接下來我們檢視一下事務的執行原始碼。

事務執行原始碼路徑:也是通過Interceptor進行實現的。

TransactionInterceptor.invoke()-》invokeWithinTransaction()

檢視invokeWithinTransaction,該方法為事務執行的流程。

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();
   // 獲取到當前方法或類上的@Transactional註解的資訊
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
   // 得到一個TransactionManager
   final TransactionManager tm = determineTransactionManager(txAttr);

   if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
      ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
         if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
            throw new TransactionUsageException(
                  "Unsupported annotated transaction on suspending function detected: " + method +
                  ". Use TransactionalOperator.transactional extensions instead.");
         }
         ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
         if (adapter == null) {
            throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
                  method.getReturnType());
         }
         return new ReactiveTransactionSupport(adapter);
      });
      return txSupport.invokeWithinTransaction(
            method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
   }

   // 轉化為PlatformTransactionManager
   PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
   // 根據當前執行的類中的某個方法以及@Transactional註解的資訊生成一個唯一標誌,這個標記會用來作為事務名
   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
         // 出現異常的情況下,如果異常需要rollback,則回滾,否則則會提交
         completeTransactionAfterThrowing(txInfo, ex);
         // 出現異常後會先執行finally中的方法邏輯在丟擲當前異常
         throw ex;
      }
      finally {
         // 清除ThreadLocal中的事務資訊
         cleanupTransactionInfo(txInfo);
      }

      if (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 {
      final ThrowableHolder throwableHolder = new ThrowableHolder();

      // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      try {
         Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
            TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
            try {
               Object retVal = invocation.proceedWithInvocation();
               if (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);
            }
         });

         // Check result state: It might indicate a Throwable to rethrow.
         if (throwableHolder.throwable != null) {
            throw throwableHolder.throwable;
         }
         return result;
      }
      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;
      }
   }
}

接下來進入createTransactionIfNecessary方法檢視事務的建立過程。

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

進入AbstractPlatformTransactionManager.getTransaction()方法

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

   // Use defaults if no transaction definition given.
   // TransactionDefinition就是TransactionAttribute,就是@Transactional註解資訊
   // 表示一個事務的定義,通過@Transactional註解也就是在定義一個事務
   TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());

   // 獲取一個事務物件,每次都會生成一個DataSourceTransactionObject,而重點是這個物件中是否已經存在一個資料庫連線
   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.
   // timeout如果為-1,表示沒有時間限制
   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 {
         boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);

         // 事務物件中包括:
         // 1. 事務的定義
         // 2. 事務物件
         // 3. 是否是新事務
         DefaultTransactionStatus status = newTransactionStatus(
               def, transaction, true, newSynchronization, debugEnabled, suspendedResources);

         // 開啟事務,獲得新的Connection物件
         doBegin(transaction, def);
         // 初始化TransactionSynchronizationManager中的屬性
         prepareSynchronization(status, def);
         return status;
      }
      catch (RuntimeException | Error ex) {
         resume(null, suspendedResources);
         throw ex;
      }
   }
   else {
      // 不會doBegin,不會真的開啟事務,也就是不會把Connection的autoCommit設定為false,sql沒有在事務中執行
      // 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);
   }
}

繼續向裡面走,看核心方法DataSourceTransactionManager.doGetTransaction(),這裡就是建立了一個DataSourceTransactionObject物件

protected Object doGetTransaction() {
   DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    // 是否允許巢狀事務,預設是允許的,在DataSourceTransactionManager無參構造方法中進行了設定
   txObject.setSavepointAllowed(isNestedTransactionAllowed());
   // 從當前執行緒ThreadLocal中獲取當前DataSource所對應的資料庫連線,可能為null
   ConnectionHolder conHolder =
         (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
   txObject.setConnectionHolder(conHolder, false);
   return txObject;
}

回到流程中去,到 doBegin方法,開啟一個事務

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

   try {
      // 如果事務物件內沒有連線就從dataSource中獲取一個連線
      if (!txObject.hasConnectionHolder() ||
            txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
         // isSynchronizedWithTransaction 我的理解,表示連線是否和事務同步,表示每開啟一個事務就從DataSource中獲取一個連線
         // 預設是false,所以在開啟事務時,除非當前事務物件中還沒有資料庫連線才會從DataSource中去獲取一個連線
         Connection newCon = obtainDataSource().getConnection();
         if (logger.isDebugEnabled()) {
            logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
         }
         // 設定為newConnectionHolder為true
         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).
      // 把資料庫連線的autoCommit設定為false,禁止資料庫的自動提交
      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()) {
         // 把新生成的資料庫連線設定到當前執行緒的TheadLocal中進行快取
         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);
   }
}

從一個小demo中總結一下流程:

@Component
public class TxService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private TxService txService;
@Transactional
public void insert11(){
    System.out.println("insert11");
    jdbcTemplate.execute("insert grade values(11,2,2,2)");
    txService.a();
}

@Transactional(propagation = Propagation.REQUIRES_NEW)    掛起test,開啟新事務,執行a  
//@Transactional(propagation = Propagation.NOT_SUPPORTED)   掛起test,不開啟事務,執行a
public void  a(){
    jdbcTemplate.execute("insert grade values(13,2,2,2)");
}
}

事務執行過程:

  1. 生成test事務狀態物件
  2. test事務doBegin,獲取並將資料庫連線2825設定到test事務狀態物件中
  3. 把test事務資訊設定到事務同步管理器中
  4. 執行test業務邏輯方法(可以獲取到test事務的資訊)
    1. 生成a事務狀態物件,並且可以獲取到當前執行緒中已經存在的資料庫連線2825
    2. 判斷出來當前執行緒中已經存在事務
    3. 如果需要新開始事務,就先掛起資料庫連線2825,掛起就是把test事務資訊從事務同步管理器中轉移到掛起資源物件中,並把當前a事務狀態物件中的資料庫連線設定為null
    4. a事務doBegin,新生成一個資料庫連線2826,並設定到a事務狀態物件中
    5. 把a事務資訊設定到事務同步管理器中
    6. 執行a業務邏輯方法(可以利用事務同步管理器獲取到a事務資訊)
    7. 利用a事務狀態物件,執行提交
    8. 提交之後會恢復所掛起的test事務,這裡的恢復,其實只是把掛起資源物件中所儲存的資訊再轉移回事務同步管理器

5.繼續執行test業務邏輯方法(仍然可以獲取到test事務的資訊)

6.利用test事務狀態物件,執行提交

關於事務的掛起和恢復說明一下:spring事務的掛起就是把事務管理同步器中的資訊轉移到掛起資源物件,恢復就是把掛起資源物件轉移回事務同步管理器中。

事務傳播型別

事務傳播機制只針對於同一執行緒,如果是多執行緒,則各使用各的事務。

 

相關文章