Spring AOP JdkDynamicAopProxy

所有文章已遷移至csdn,csdn個人主頁bugpool.blog.csdn.net

Spring AOP源碼目錄

Spring AOP源碼01:Jdk動態(tài)代理底層源碼
Spring AOP源碼02:ProxyFactory
Spring AOP源碼03:JdkDynamicAopProxy
Spring AOP源碼04:MethodInvocation 攔截器調(diào)用
Spring AOP源碼05:DefaultAdvisorAutoProxyCreator
Spring期末考壓軸題:當Spring AOP遇上循環(huán)依賴
git注釋源碼地址:https://github.com/chaitou/spring-framework-master.git

前言

如下圖,結(jié)合第二篇Spring AOP核心源碼 ProxyFactory育八,Spring AOP動態(tài)代理有2中生成方式稽揭,當代理對象實現(xiàn)了接口且沒有配置強制使用cglib代理時翔忽,將使用JdkDynamicAopProxy生成代理呼渣。反之使用CglibAopProxy生成代理蔑祟。同時我們在第一篇Jdk動態(tài)代理底層源碼中已知早敬,Jdk動態(tài)代理通過getProxy生成代理忌傻,同時$proxy代理對象在調(diào)用方法時,將會回調(diào)invoke方法進行搞监。因此對于JdkDynamicAopProxy來說水孩,最重要的代碼就是分析getProxyinvoke方法。至于生成代理的底層源碼琐驴,第一篇已經(jīng)介紹過俘种,這里不再贅述

AopProxy

源碼分析

getProxy

下面代碼在第一篇Jdk動態(tài)代理底層源碼中我們手動寫過一遍秤标,這里再復習一下

    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isTraceEnabled()) {
            logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
        }
        // 獲取代理接口
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        // 判斷接口是否又hashCode和equals方法
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        // 使用JDK代理(classLoader, 接口, 當前JdkDynamicAopProxy對象:用于回調(diào)invoke和target對象方法)
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
  1. classLoader:當前類加載器
  2. proxiedInterfaces:當前代理類所實現(xiàn)的接口數(shù)組
  3. this:將自身類對象傳給生成的代理,作為代理的屬性h宙刘,屬性h將被用于方法回調(diào)觸發(fā)h.invoke方法苍姜,實現(xiàn)增強

invoke

在開始invoke之前,先定一下目標悬包,為了抓住主干衙猪,本專題只關(guān)心普通增強引介增強等將暫時跳過布近,另外獨立出章節(jié)詳解垫释,因此遇到引介增強等可以暫時先不糾結(jié)

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        // 獲取代理目標對象
        TargetSource targetSource = this.advised.targetSource;
        Object target = null;

        try {
            // equals方法處理
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            // hashCode處理
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            // Advised接口或者其父接口中定義的方法,直接反射調(diào)用
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;
            // 目標對象內(nèi)部調(diào)用是無法實現(xiàn)增強的,如果exposeProxy設(shè)置為true撑瞧,需要暴露代理
            // ThreadLocal<Object> currentProxy
            // 對象是ThreadLocal饶号,在finally后會清除currentProxy
            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            // 以防target來自對象池,所以在創(chuàng)建代理前調(diào)用get獲取target
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);

            // Get the interception chain for this method.
            // 1. 獲取攔截器鏈
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            // 攔截器為空季蚂,直接調(diào)用切點方法
            if (chain.isEmpty()) {
                // 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 = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            else {
                // We need to create a method invocation...
                // 2. 將攔截器統(tǒng)一封裝成ReflectiveMethodInvocation
                MethodInvocation invocation =
                        new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                // 3. 執(zhí)行攔截器鏈
                retVal = invocation.proceed();
            }

            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target &&
                    returnType != Object.class && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                retVal = proxy;
            }
            else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        "Null return value from advice does not match primitive return type for: " + method);
            }
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                // Must have come from TargetSource.
                // 如果自定義了TargetSource茫船,釋放target資源池由子類實現(xiàn)
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

1. 獲取攔截器鏈

該方法將獲取到所有與當前method匹配的advice(增強),跟蹤getInterceptorsAndDynamicInterceptionAdvice代碼扭屁,發(fā)現(xiàn)Spring AOP也使用緩存進行提高性能算谈,如果該方法已經(jīng)獲取過攔截器,則直接取緩存料滥,否則通過advisorChainFactory獲取攔截器鏈

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
        MethodCacheKey cacheKey = new MethodCacheKey(method);
        // 從緩存中獲取
        List<Object> cached = this.methodCache.get(cacheKey);
        if (cached == null) {
            // 獲取攔截器鏈
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    this, method, targetClass);
            // 設(shè)置緩存
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
    }

繼續(xù)跟中getInterceptorsAndDynamicInterceptionAdvice方法

// DefaultAdvisorChainFactory.java
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {

    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, @Nullable Class<?> targetClass) {

        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        Advisor[] advisors = config.getAdvisors();
        List<Object> interceptorList = new ArrayList<>(advisors.length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        Boolean hasIntroductions = null;

        // 循環(huán)所有切面
        for (Advisor advisor : advisors) {
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                // 校驗當前Advisor是否適用于當前對象
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    boolean match;
                    // 校驗Advisor是否應用到當前方法上
                    if (mm instanceof IntroductionAwareMethodMatcher) {
                        if (hasIntroductions == null) {
                            hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                        }
                        match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                    }
                    else {
                        match = mm.matches(method, actualClass);
                    }
                    // 匹配成功
                    if (match) {
                        // 從advisor中獲取攔截器數(shù)組
                        MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                // 添加動態(tài)攔截器
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            // 添加普通攔截器
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            // 引介增強
            else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            // 其他類型的advisor
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }

        return interceptorList;
    }
    ...
}

剖去引介增強然眼,動態(tài)增強,我們只關(guān)心普通攔截器葵腹。整個代碼思路還是比較清晰的高每,獲取所有Advisor(切面),通過pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)校驗當前代理對象是否匹配該Advisor践宴,再通過pointcutAdvisor.getPointcut().getMethodMatcher()校驗是否匹配當前調(diào)用method鲸匿。如果通過校驗,則提取advisor中的interceptors攔截器阻肩,添加到interceptorList

2. 將攔截器封裝成ReflectiveMethodInvocation

一個構(gòu)造器的調(diào)用带欢,重點在下一步調(diào)用

    protected ReflectiveMethodInvocation(
            Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
            @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

        this.proxy = proxy;
        this.target = target;
        this.targetClass = targetClass;
        this.method = BridgeMethodResolver.findBridgedMethod(method);
        this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
    }

3. 執(zhí)行攔截器鏈

// 執(zhí)行攔截器鏈
retVal = invocation.proceed();
    public Object proceed() throws Throwable {
        // We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // Evaluate dynamic method matcher here: static part will already have
            // been evaluated and found to match.
            // 動態(tài)匹配
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
            if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // Dynamic matching failed.
                // Skip this interceptor and invoke the next in the chain.
                // 不匹配,則跳過
                return proceed();
            }
        }
        else {
            // It's an interceptor, so we just invoke it: The pointcut will have
            // been evaluated statically before this object was constructed.
            // 普通攔截器烤惊,直接觸發(fā)
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

同樣我們只關(guān)心普通攔截器乔煞,但是看到普通攔截器僅僅只有一行代碼

return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

這一行代碼就可以讓攔截器按照Before、After甚至是用戶自定義的Order順序進行鏈式調(diào)用柒室,這也太神奇了吧渡贾?下一節(jié)我們將挑出AspectJAfterAdviceMethodBeforeAdviceInterceptor探究攔截器是如何實現(xiàn)增強與method之前的有序調(diào)用

代理方法調(diào)用流程圖:


在這里插入圖片描述
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市雄右,隨后出現(xiàn)的幾起案子空骚,更是在濱河造成了極大的恐慌锦溪,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件府怯,死亡現(xiàn)場離奇詭異,居然都是意外死亡防楷,警方通過查閱死者的電腦和手機牺丙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來复局,“玉大人冲簿,你說我怎么就攤上這事∫诨瑁” “怎么了峦剔?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長角钩。 經(jīng)常有香客問我吝沫,道長,這世上最難降的妖魔是什么递礼? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任惨险,我火速辦了婚禮,結(jié)果婚禮上脊髓,老公的妹妹穿的比我還像新娘辫愉。我一直安慰自己,他們只是感情好将硝,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布恭朗。 她就那樣靜靜地躺著,像睡著了一般依疼。 火紅的嫁衣襯著肌膚如雪痰腮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天律罢,我揣著相機與錄音诽嘉,去河邊找鬼。 笑死弟翘,一個胖子當著我的面吹牛虫腋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稀余,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼悦冀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了睛琳?” 一聲冷哼從身側(cè)響起盒蟆,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤踏烙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后历等,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讨惩,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年寒屯,在試婚紗的時候發(fā)現(xiàn)自己被綠了荐捻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡寡夹,死狀恐怖处面,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情菩掏,我是刑警寧澤魂角,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站智绸,受9級特大地震影響野揪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瞧栗,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一囱挑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沼溜,春花似錦平挑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至找都,卻和暖如春唇辨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背能耻。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工赏枚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晓猛。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓饿幅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親戒职。 傳聞我的和親對象是個殘疾皇子栗恩,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 有關(guān)AOP相關(guān)概念以及Spring AOP相關(guān)概念和Spring AOP的使用不再重復。關(guān)于AOP在Spring中...
    加大裝益達閱讀 991評論 0 2
  • 通過schema形式使用spring aop時洪燥,spring提供了相應的拓展磕秤,可以解析自定義的標簽乳乌。下面是基于sc...
    shallowinggg閱讀 1,152評論 0 0
  • 本學習筆記將盡可能的將AOP的知識講解的通俗易懂,先從一個典型的問題出發(fā)市咆,引入AOP這個概念汉操,介紹AOP的基本概念...
    jwfy閱讀 25,808評論 1 24
  • 所有文章已遷移至csdn,csdn個人主頁bugpool.blog.csdn.net Spring AOP源碼目錄...
    BugPool閱讀 1,068評論 0 0
  • 2020年1月3日 星期五 小雨 今天下班后園所所有成員一起聚餐蒙兰,5點半準時出發(fā)磷瘤,7點才到達目的地...
    雨辰_7446閱讀 138評論 0 1