Spring 的 -Transactional 如何實現的

@Transactional 註解簡介

@Transactional是 spring 中聲明式事務管理的註解配置方式,相信這個註解的作用大家都很清楚。@Transactional註解可以幫助我們把事務開啓、提交或者回滾的操作,通過 aop 的方式進行管理。

通過@Transactional註解就能讓 spring 爲我們管理事務,免去了重複的事務管理邏輯,減少對業務代碼的侵入,使我們開發人員能夠專注於業務層面開發。

我們知道實現 @Transactional 原理是基於 spring aop,aop 又是動態代理模式的實現,通過對源碼的閱讀,總結出下面的步驟來了解實際中,在 spring 是如何利用 aop 來實現 @Transactional 的功能的。

spring 中聲明式事務實現原理猜想

首先,對於 spring 中 aop 實現原理有了解的話,應該知道想要對一個方法進行代理的話,肯定需要定義切點。在 @Transactional 的實現中,同樣如此,spring 爲我們定義了以 @Transactional 註解爲植入點的切點,這樣才能知道 @Transactional 註解標註的方法需要被代理。

有了切面定義之後,在 spring 的 bean 的初始化過程中,就需要對實例化的 bean 進行代理,並且生成代理對象。

生成代理對象的代理邏輯中,進行方法調用時,需要先獲取切面邏輯,@Transactional 註解的切面邏輯類似於 @Around,在 spring 中是實現一種類似代理邏輯。

@Transactional 作用

根據上面的原理猜想,下面簡單介紹每個步驟的源碼以進行驗證。

首先是 @Transactional,作用是定義代理植入點。我們知道代理對象創建的通過BeanPostProcessor的實現類AnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInstantiation方法來實現個,如果需要進行代理,那麼在這個方法就會返回一個代理對象給容器,同時判斷植入點也是在這個方法中。

那麼下面開始分析,在配置好註解驅動方式的事務管理之後,spring 會在 ioc 容器創建一個BeanFactoryTransactionAttributeSourceAdvisor實例,這個實例可以看作是一個切點,在判斷一個 bean 在初始化過程中是否需要創建代理對象,都需要驗證一次BeanFactoryTransactionAttributeSourceAdvisor是否是適用這個 bean 的切點。如果是,就需要創建代理對象,並且把BeanFactoryTransactionAttributeSourceAdvisor實例注入到代理對象中。

前文我們知道在AopUtils#findAdvisorsThatCanApply中判斷切面是否適用當前 bean,可以在這個地方斷點分析調用堆棧,AopUtils#findAdvisorsThatCanApply一致調用,最終通過以下代碼判斷是否適用切點。

如果您正在學習 Spring Boot,那麼推薦一個連載多年還在繼續更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/

@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
    //這裏就是分析Method是否被@Transactional註解標註,有的話,不用說BeanFactoryTransactionAttributeSourceAdvisor適配當前bean,進行代理,並且注入切點
    //BeanFactoryTransactionAttributeSourceAdvisor
   AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
   if (attributes != null) {
      return parseTransactionAnnotation(attributes);
   }
   else {
      return null;
   }
}

上面就是判斷是否需要根據 @Transactional 進行代理對象創建的判斷過程。@Transactional 的作用一個就是標識方法需要被代理,一個就是攜帶事務管理需要的一些屬性信息。

動態代理邏輯實現

【aop 實現原理分析】中知道,aop 最終的代理對象的代理方法是

所以我們可以在這個方法斷點分析代理邏輯。

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;
   Class<?> targetClass = null;
   Object target = null;
   try {
      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      // May be null. Get as late as possible to minimize the time we
      // "own" the target, in case it comes from a pool...
      target = getTarget();
      if (target != null) {
         targetClass = target.getClass();
      }
       //follow
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
         // We need to create a method invocation...
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null) {
         releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

如果您正在學習 Spring Boot,那麼推薦一個連載多年還在繼續更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/

通過分析 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)返回的是 TransactionInterceptor, 利用 TransactionInterceptor 是如何實現代理邏輯調用的?

跟蹤new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

發現最終是調用TransactionInterceptor#invoke方法,並且把 CglibMethodInvocation 注入到 invoke 方法中,從上面可以看到CglibMethodInvocation是包裝了目標對象的方法調用的所有必須信息,因此,在TransactionInterceptor#invoke裏面也是可以調用目標方法的,並且還可以實現類似 @Around 的邏輯,在目標方法調用前後繼續注入一些其他邏輯,比如事務管理邏輯。

TransactionInterceptor–最終事務管理者

下面看代碼。

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
 // Work out the target class: may be {@code null}.
 // The TransactionAttributeSource should be passed the target class
 // as well as the method, which may be from an interface.
 Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

 // Adapt to TransactionAspectSupport's invokeWithinTransaction...
 return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
  @Override
  public Object proceedWithInvocation() throws Throwable {
   return invocation.proceed();
  }
 });
}

繼續跟蹤 invokeWithinTransaction,下面的代碼中其實就可以看出一些邏輯端倪,就是我們猜想的實現方式,事務管理。

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

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

   else {
      // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
      try {
         Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
               new TransactionCallback<Object>() {
                  @Override
                  public Object doInTransaction(TransactionStatus status) {
                     TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                     try {
                        return invocation.proceedWithInvocation();
                     }
                     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.
                           return new ThrowableHolder(ex);
                        }
                     }
                     finally {
                        cleanupTransactionInfo(txInfo);
                     }
                  }
               });

         // Check result: It might indicate a Throwable to rethrow.
         if (result instanceof ThrowableHolder) {
            throw ((ThrowableHolder) result).getThrowable();
         }
         else {
            return result;
         }
      }
      catch (ThrowableHolderException ex) {
         throw ex.getCause();
      }
   }
}

如果您正在學習 Spring Boot,那麼推薦一個連載多年還在繼續更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/

總結

最終可以總結一下整個流程,跟開始的猜想對照。

分析源碼後對照

來源:https://blog.csdn.net/qq_20597727/

article/details/84868035

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/wyrjM3d6N26gQ6_FbNE90Q