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

          面試官:@Transactional 注解是如何實現(xiàn)的?面試必問!

          共 17354字,需瀏覽 35分鐘

           ·

          2021-09-26 11:05

          @Transactional注解簡介

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

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

          圖片

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

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

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

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

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

          圖片

          @Transactional作用

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

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

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

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

          • AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class targetClass) 這里可以根據(jù)參數(shù)打上條件斷點進行調(diào)試分析調(diào)用棧,targetClass就是目標class …一系列調(diào)用
          • 最終SpringTransactionAnnotationParser#parseTransactionAnnotation(java.lang.reflect.AnnotatedElement)
          @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;
             }
          }

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

          動態(tài)代理邏輯實現(xiàn)

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

          • DynamicAdvisedInterceptor#intercept

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

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

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

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

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

          TransactionInterceptor–最終事務管理者

          下面看代碼。

          • 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,下面的代碼中其實就可以看出一些邏輯端倪,就是我們猜想的實現(xiàn)方式,事務管理。

          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.
                    //方法調(diào)用
                   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();
                }
             }
          }

          總結(jié)

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

          圖片

          分析源碼后對照

          圖片

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


          程序汪資料鏈接

          程序汪接的7個私活都在這里,經(jīng)驗整理

          Java項目分享  最新整理全集,找項目不累啦 04版

          堪稱神級的Spring Boot手冊,從基礎入門到實戰(zhàn)進階

          臥槽!字節(jié)跳動《算法中文手冊》火了,完整版 PDF 開放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!

          字節(jié)跳動總結(jié)的設計模式 PDF 火了,完整版開放下載!

          歡迎添加程序汪個人微信 itwang007  進粉絲群或圍觀朋友圈

          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  无码视频高清亚洲 | 干逼视频免费 | 水蜜桃成人网 | 国产黄色免费在线观看 | 国产老熟女高潮毛片A片仙踪林 |