<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    你知道事務(wù)注解@Transactional的實(shí)現(xiàn)原理嗎?

    共 2540字,需瀏覽 6分鐘

     ·

    2021-09-22 02:28

    點(diǎn)擊“藍(lán)字”,關(guān)注,置頂公眾號(hào)

    每日技術(shù)干貨,第一時(shí)間送達(dá)!


     

    1

    @Transactional注解簡(jiǎn)介

    @Transactional是spring中聲明式事務(wù)管理的注解配置方式,相信這個(gè)注解的作用大家都很清楚。@Transactional注解可以幫助我們把事務(wù)開(kāi)啟、提交或者回滾的操作,通過(guò)aop的方式進(jìn)行管理。

    通過(guò)@Transactional注解就能讓spring為我們管理事務(wù),免去了重復(fù)的事務(wù)管理邏輯,減少對(duì)業(yè)務(wù)代碼的侵入,使我們開(kāi)發(fā)人員能夠?qū)W⒂跇I(yè)務(wù)層面開(kāi)發(fā)。

    我們知道實(shí)現(xiàn)@Transactional原理是基于spring aop,aop又是動(dòng)態(tài)代理模式的實(shí)現(xiàn),通過(guò)對(duì)源碼的閱讀,總結(jié)出下面的步驟來(lái)了解實(shí)際中,在spring 是如何利用aop來(lái)實(shí)現(xiàn)@Transactional的功能的。


    2

    spring中聲明式事務(wù)實(shí)現(xiàn)原理猜想

    首先,對(duì)于spring中aop實(shí)現(xiàn)原理有了解的話,應(yīng)該知道想要對(duì)一個(gè)方法進(jìn)行代理的話,肯定需要定義切點(diǎn)。在@Transactional的實(shí)現(xiàn)中,同樣如此,spring為我們定義了以 @Transactional 注解為植入點(diǎn)的切點(diǎn),這樣才能知道@Transactional注解標(biāo)注的方法需要被代理。

    有了切面定義之后,在spring的bean的初始化過(guò)程中,就需要對(duì)實(shí)例化的bean進(jìn)行代理,并且生成代理對(duì)象。

    生成代理對(duì)象的代理邏輯中,進(jìn)行方法調(diào)用時(shí),需要先獲取切面邏輯,@Transactional注解的切面邏輯類(lèi)似于@Around,在spring中是實(shí)現(xiàn)一種類(lèi)似代理邏輯。



    3

    @Transactional作用

    根據(jù)上面的原理猜想,下面簡(jiǎn)單介紹每個(gè)步驟的源碼以進(jìn)行驗(yàn)證。

    首先是@Transactional,作用是定義代理植入點(diǎn)。我們知道代理對(duì)象創(chuàng)建的通過(guò)BeanPostProcessor的實(shí)現(xiàn)類(lèi)AnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInstantiation方法來(lái)實(shí)現(xiàn)個(gè),如果需要進(jìn)行代理,那么在這個(gè)方法就會(huì)返回一個(gè)代理對(duì)象給容器,同時(shí)判斷植入點(diǎn)也是在這個(gè)方法中。

    那么下面開(kāi)始分析,在配置好注解驅(qū)動(dòng)方式的事務(wù)管理之后,spring會(huì)在ioc容器創(chuàng)建一個(gè)BeanFactoryTransactionAttributeSourceAdvisor實(shí)例,這個(gè)實(shí)例可以看作是一個(gè)切點(diǎn),在判斷一個(gè)bean在初始化過(guò)程中是否需要?jiǎng)?chuàng)建代理對(duì)象,都需要驗(yàn)證一次BeanFactoryTransactionAttributeSourceAdvisor是否是適用這個(gè)bean的切點(diǎn)。如果是,就需要?jiǎng)?chuàng)建代理對(duì)象,并且把BeanFactoryTransactionAttributeSourceAdvisor實(shí)例注入到代理對(duì)象中。

    前文我們知道在AopUtils#findAdvisorsThatCanApply中判斷切面是否適用當(dāng)前bean,可以在這個(gè)地方斷點(diǎn)分析調(diào)用堆棧,AopUtils#findAdvisorsThatCanApply一致調(diào)用,最終通過(guò)以下代碼判斷是否適用切點(diǎn)。

    • AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class targetClass) 這里可以根據(jù)參數(shù)打上條件斷點(diǎn)進(jìn)行調(diào)試分析調(diào)用棧,targetClass就是目標(biāo)class …一系列調(diào)用
    • 最終SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
    @Override
    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
        //這里就是分析Method是否被@Transactional注解標(biāo)注,有的話,不用說(shuō)BeanFactoryTransactionAttributeSourceAdvisor適配當(dāng)前bean,進(jìn)行代理,并且注入切點(diǎn)
        //BeanFactoryTransactionAttributeSourceAdvisor
       AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
       if (attributes != null) {
          return parseTransactionAnnotation(attributes);
       }
       else {
          return null;
       }
    }

    上面就是判斷是否需要根據(jù)@Transactional進(jìn)行代理對(duì)象創(chuàng)建的判斷過(guò)程。@Transactional的作用一個(gè)就是標(biāo)識(shí)方法需要被代理,一個(gè)就是攜帶事務(wù)管理需要的一些屬性信息。


    4

    動(dòng)態(tài)代理邏輯實(shí)現(xiàn)

    【aop實(shí)現(xiàn)原理分析】中知道,aop最終的代理對(duì)象的代理方法是

    • DynamicAdvisedInterceptor#intercept

    所以我們可以在這個(gè)方法斷點(diǎn)分析代理邏輯。

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

    通過(guò)分析 Listchain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)返回的是TransactionInterceptor,利用TransactionInterceptor是如何實(shí)現(xiàn)代理邏輯調(diào)用的?

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

    發(fā)現(xiàn)最終是調(diào)用TransactionInterceptor#invoke方法,并且把CglibMethodInvocation注入到invoke方法中,從上面可以看到CglibMethodInvocation是包裝了目標(biāo)對(duì)象的方法調(diào)用的所有必須信息,因此,在TransactionInterceptor#invoke里面也是可以調(diào)用目標(biāo)方法的,并且還可以實(shí)現(xiàn)類(lèi)似@Around的邏輯,在目標(biāo)方法調(diào)用前后繼續(xù)注入一些其他邏輯,比如事務(wù)管理邏輯。


    5

    TransactionInterceptor–最終事務(wù)管理者

    下面看代碼。

    • TransactionInterceptor#invoke
    @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();
      }
     });
    }

    繼續(xù)跟蹤invokeWithinTransaction,下面的代碼中其實(shí)就可以看出一些邏輯端倪,就是我們猜想的實(shí)現(xiàn)方式,事務(wù)管理。

    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.
           //開(kāi)啟事務(wù)
          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.
              //方法調(diào)用
             retVal = invocation.proceedWithInvocation();
          }
          catch (Throwable ex) {
             // target invocation exception
         //回滾事務(wù)
             completeTransactionAfterThrowing(txInfo, ex);
             throw ex;
          }
          finally {
             cleanupTransactionInfo(txInfo);
          }
           //提交事務(wù)
          commitTransactionAfterReturning(txInfo);
          return retVal;
       }

       else {
          // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
          try {
             Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
                   new TransactionCallback() {
                      @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();
          }
       }
    }



    6

    總結(jié)

    最終可以總結(jié)一下整個(gè)流程,跟開(kāi)始的猜想對(duì)照。

    分析源碼后對(duì)照


    原文鏈接:https://blog.csdn.net/qq_20597727/article/details/84868035


    往期推薦






    Java 程序員必須掌握的 10 款開(kāi)源工具!

    程序員必備排查日志 9 大類(lèi)命令詳解

    這幾種全局ID生成方式,你知道哪幾種?

    高效率編碼小技巧,帶你飛!

    如果保證多線程場(chǎng)景下的線程安全?

    IDEA這么優(yōu)化后,代碼跑得嗖嗖快


    瀏覽 97
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    亚洲狼人综合网 | 欧美中文大屌 | 操逼大片 | 国产原创AV成人网站 | 美女免费黄片 |