AOP原理分析(二)執(zhí)行流程

前言

根據(jù)上篇文章的介紹,當(dāng)我們創(chuàng)建好AOP的代理對(duì)象后,當(dāng)增強(qiáng)的方法被調(diào)用后,就會(huì)走到DynamicAdvisedInterceptor的intercept方法的邏輯,也正是在此Spring會(huì)組裝好對(duì)調(diào)用方法的攔截器鏈,然后通過責(zé)任鏈模式執(zhí)行,從而實(shí)現(xiàn)攔截目標(biāo)方法的邏輯。

AOP執(zhí)行的三大步

總覽AOP執(zhí)行流程

首先,一起看看代理攔截的執(zhí)行邏輯吧

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Object target = null;
            TargetSource targetSource = this.advised.getTargetSource();
            try {//exposeProxy:暴露代理對(duì)象,使用了代理對(duì)象就有了增強(qiáng)功能
                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);//使用ThreadLocal線程共享這個(gè)代理對(duì)象
                    setProxyContext = true;
                }
                target = targetSource.getTarget();
                Class<?> targetClass = (target != null ? target.getClass() : null);
                //責(zé)任鏈模式在執(zhí)行目標(biāo)方法前后執(zhí)行其他的通知方法
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//通知方法轉(zhuǎn)攔截器鏈
                Object retVal;
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    // 直接反射執(zhí)行目標(biāo)方法
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                }
                else {
                    //創(chuàng)建一個(gè)方法執(zhí)行的東西(攔截器鏈在此執(zhí)行) 
                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                }
                retVal = processReturnType(proxy, target, method, retVal);
                return retVal;
            }
            finally {
                if (target != null && !targetSource.isStatic()) {
                    targetSource.releaseTarget(target);
                }
                if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }

其實(shí),總體看下來,這個(gè)方法主要干了三件事:

  1. 根據(jù)exposeProxy的值來決定是否暴露當(dāng)前的代理對(duì)象
  2. 將當(dāng)前執(zhí)行方法的增強(qiáng)器轉(zhuǎn)換為攔截器鏈
  3. 責(zé)任鏈模式執(zhí)行攔截器鏈邏輯
    既然干了哪三件事情我們已經(jīng)知道了,那我們接下來就挨個(gè)的來看Spring是如何干這三件事的:

第一步:是否暴露代理對(duì)象

在開啟AOP的注解模式的@EnableAspectJAutoProxy注解中有一個(gè)exposeProxy屬性,決定了是否暴露生成的代理對(duì)象,默認(rèn)是false的,即不暴露。當(dāng)我們?cè)O(shè)置exposeProxy=true后,就會(huì)觸發(fā)AopContext.setCurrentProxy(proxy)的邏輯,我們看一下AopContext的代碼:

    public final class AopContext {

    private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");

    private AopContext() {
    }

    public static Object currentProxy() throws IllegalStateException {
        Object proxy = currentProxy.get();
        if (proxy == null) {
            throw new IllegalStateException(
                    "Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " +
                            "ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.");
        }
        return proxy;
    }

    @Nullable
    static Object setCurrentProxy(@Nullable Object proxy) {
        Object old = currentProxy.get();
        if (proxy != null) {
            currentProxy.set(proxy);
        }
        else {
            currentProxy.remove();
        }
        return old;
    }

}

主要是使用了ThreadLocal來存儲(chǔ)了生成的代理對(duì)象,這樣我們就可以在當(dāng)前線程中拿到這個(gè)線程對(duì)象進(jìn)行其他操作了。有人會(huì)問:有什么實(shí)際的應(yīng)用嗎?還真有,其實(shí)這是我們后面會(huì)談的Spring的事務(wù)也是基于AOP這一套的代理攔截模式的,我們知道Spring的事務(wù)如果方法內(nèi)調(diào)用就會(huì)導(dǎo)致事務(wù)失效,這也很好理解,因?yàn)榉鞘聞?wù)方法內(nèi)部調(diào)用事務(wù)方法時(shí),只會(huì)直接反射調(diào)用執(zhí)行非事務(wù)方法的邏輯,執(zhí)行到事務(wù)方法時(shí)是不會(huì)再走代理攔截的邏輯了,那我們?nèi)绾巫屗沧叽頂r截的邏輯呢,那就需要通過代理類來調(diào)用事務(wù)方法才行,那這個(gè)代理類我們就可以在AopContext中獲取,即拿到當(dāng)前暴露的代理對(duì)象。

第二步:增強(qiáng)器轉(zhuǎn)換為攔截器鏈

我們需要將當(dāng)前執(zhí)行方法的增強(qiáng)器Advice轉(zhuǎn)換為攔截器(MethodInterceptor)鏈,因?yàn)樽罱K的執(zhí)行是通過責(zé)任鏈的模式來進(jìn)行攔截執(zhí)行的,所以首先需要將攔截器鏈準(zhǔn)備好,我們來看下整個(gè)轉(zhuǎn)換為攔截器的整個(gè)流程(如下圖):


Advice轉(zhuǎn)換流程.png

從上圖中,我們可以看到整個(gè)的轉(zhuǎn)換流程為:
先是拿到所有的Advisor,然后更具Advisor的類型分別來判斷這個(gè)Advisor是否能攔截當(dāng)前執(zhí)行的方法,上文中介紹Advisor的時(shí)候,我們知道Spring中提供了兩個(gè)子接口,分別是PointcutAdvisorIntroductionAdvisor,兩者的區(qū)別我們也知道:PointcutAdvisor是方法級(jí)別的切入,所以需要先驗(yàn)證類過濾器然后再驗(yàn)證方法匹配器,都通過了再進(jìn)行轉(zhuǎn)攔截器;而IntroductionAdvisor是類級(jí)別的切入,所以我們只需要驗(yàn)證類過濾器通過就可以進(jìn)行轉(zhuǎn)攔截器了。
Spring提供了DefaultAdvisorAdapterRegistry這個(gè)類來幫助我們將Advice轉(zhuǎn)換為攔截器,而所謂的攔截器就是Spring家定義的MethodInterceptor接口,在Spring中Advice并不是都是MethodInterceptor類型的,AOP中的AspectJMethodBeforeAdvice和AspectJAfterReturningAdvice就沒有直接實(shí)現(xiàn)MethodInterceptor,所以在轉(zhuǎn)換的時(shí)候,提供了兩個(gè)適配器來適配這些不是MethodInterceptor類型的Advice。
我相信很多小伙伴都這里都會(huì)問:為什么有的Advice不直接實(shí)現(xiàn)MethodInterceptor呢?如果我們自己擴(kuò)展一個(gè)Advice,那我肯定直接實(shí)現(xiàn)MethodInterceptor啊,這多方便,而Spring自己卻有的需要進(jìn)行適配器進(jìn)行適配呢?這個(gè)問題得問Spring的開發(fā)人員了,兩種方式都可以實(shí)現(xiàn),獲取只是Spring開發(fā)人員告訴你這里可以使用適配器模式吧哈哈哈,也說不定,后面版本就把它替換了呢,咱不糾結(jié)這個(gè)小問題。

構(gòu)建責(zé)任鏈模式

有了攔截鏈集合,然后就是需要構(gòu)建出責(zé)任鏈的調(diào)用了。
責(zé)任鏈調(diào)用的核心是在構(gòu)建責(zé)任鏈處理器,這個(gè)處理器控制了整個(gè)責(zé)任鏈的流轉(zhuǎn)調(diào)用,代碼如下:

    public Object proceed() throws Throwable {
        //判斷當(dāng)前攔截器的索引有沒有超過 攔截器總數(shù)量-1
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();//調(diào)用目標(biāo)方法
        }
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);//默認(rèn)值為-1所以先++
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { 
            // 執(zhí)行帶參數(shù)的動(dòng)態(tài)方法匹配
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
            // 執(zhí)行帶參數(shù)的動(dòng)態(tài)方法匹配
            if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {.
                // 動(dòng)態(tài)參數(shù)匹配失敗,則跳過該攔截器
                return proceed();
            }
        }
        else { // MethodInterceptor類型的,不需要進(jìn)行帶參數(shù)的動(dòng)態(tài)校驗(yàn)了
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); // 傳入責(zé)任鏈處理器對(duì)象是關(guān)鍵
        }
    }

責(zé)任鏈處理器對(duì)象中,有著攔截器鏈的集合interceptorsAndDynamicMethodMatchers,以及一個(gè)攔截器鏈執(zhí)行到哪的計(jì)數(shù)索引currentInterceptorIndex,其中需要注意的同樣也是實(shí)現(xiàn)攔責(zé)任鏈模式的核心就是執(zhí)行攔截器邏輯時(shí),需要傳入處理器對(duì)象,這樣才能調(diào)回到處理器的邏輯中,繼續(xù)進(jìn)行下一個(gè)攔截器鏈的調(diào)用,從而一環(huán)一環(huán)的調(diào)用下去。我們可以發(fā)現(xiàn),AOP的目標(biāo)方法是在所有的攔截器都走過了的最后才觸發(fā)調(diào)用的,我們總體看一下AOP的一個(gè)調(diào)用流程吧(如下圖):


AOP責(zé)任鏈執(zhí)行流程.png

寫在最后

其實(shí),聊到這里整個(gè)AOP的執(zhí)行流程基本上就講完了,但其實(shí)關(guān)于AOP的方法論才剛剛開始,從AOP看來Spring為我們提供了一種攔截代理對(duì)象然后進(jìn)行過濾,構(gòu)建責(zé)任鏈插入自定義邏輯的規(guī)范框架,我們可以在這個(gè)規(guī)范之上,進(jìn)行二次開發(fā),能夠擴(kuò)展出許多我們自己業(yè)務(wù)場(chǎng)景需要的,下節(jié),我們繼續(xù)聊聊Spring提供的這個(gè)模式是這么樣的,以及Spring中還有哪些功能點(diǎn)套用了這一套模式。從功能點(diǎn)總結(jié)出思想,這樣我們掌握了思想就是掌握了一類功能點(diǎn)的原理,對(duì)于我們二次開發(fā)的幫助是十分大的,我相信下一節(jié)的AOP模式總結(jié)對(duì)你幫助很大,敬請(qǐng)期待吧......

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容