<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你知道事務(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注解的切面邏輯類似于@Around,在spring中是實(shí)現(xiàn)一種類似代理邏輯。



          3

          @Transactional作用

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

          首先是@Transactional,作用是定義代理植入點(diǎn)。我們知道代理對(duì)象創(chuàng)建的通過(guò)BeanPostProcessor的實(shí)現(xiàn)類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)類似@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 大類命令詳解

          這幾種全局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="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久精品四区 | 日韩一片| 亚洲男女激情91免费网站 | 久久婷婷国产视频 | 成人免费视频黄色 |