Spring 源碼之AOP 原理(下)

SpringAOP的實現(xiàn)有 jdk 動態(tài)代理和 cglib 代理贱纠,對應(yīng)的核心類是 JdkDynamicAopProxy 和CglibAopProxy皆看。

先來看 JdkDynamicAopProxy偏陪,找到它的 invoke方法,上碼:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    // 最終操作的是 TargetSource對象
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        // 不代理 equals 和 hashCode 方法赐稽,調(diào)用 JdkDynamicAopProxy中的equal比較和hashCode方法
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 如果 method是在 advised中聲明的瞧哟,則把 method轉(zhuǎn)到 advised對象中使用
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
        if (this.advised.exposeProxy) {
            // 如果暴露代理對象亭引,則把proxy設(shè)置到 ThreadLocal 中贞岭,線程內(nèi)可共享該對象
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 獲取方法的攔截器鏈
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        if (chain.isEmpty()) {
            // 攔截器鏈為空八毯,則適配參數(shù),直接調(diào)用目標方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 創(chuàng)建 ReflectiveMethodInvocation瞄桨,去執(zhí)行前置话速、后置等增強器
            MethodInvocation invocation =
            new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 驅(qū)動執(zhí)行所有攔截器
            retVal = invocation.proceed();
        }

        // 返回值處理
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
        returnType != Object.class && returnType.isInstance(proxy) 
&&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                throw new AopInvocationException(
                        " ……");
            }
            return retVal;
        }
        finally {
        ……
    }

我們來重點分析 ReflectiveMethodInvocation#proceed() 方法:

public Object proceed() throws Throwable {
    // 攔截器執(zhí)行完了,就執(zhí)行目標對象的目標方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    
    // 獲取責任鏈下一個 MethodInterceptor, 對目標方法進行增強處理
    Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
    // 方法動態(tài)匹配成功芯侥,才進行增強處理
    if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
        return dm.interceptor.invoke(this);
    }
    else {
        // 動態(tài)匹配失敗泊交,跳過當前攔截器乳讥,跳到下一個     
        return proceed();
    }
  }
  else {
        // 不需要動態(tài)匹配,則直接調(diào)用 MethodInterceptor 的invoke方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }
}

我們來看 ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this) 廓俭,這里是每個interceptor都去invoke一遍云石,我們先看 MethodBeforeAdviceInterceptor#invoke()

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {

    private final MethodBeforeAdvice advice;
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }
    
    @Override
    @Nullable
    public Object invoke(MethodInvocation mi) throws Throwable {
        // 先執(zhí)行 MethodBeforeAdvice 這個 advice,而advice.before 的這個before,          
       //正是我們定義的前置通知的方法體
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        // 再遞歸執(zhí)行 MethodInvocation
        return mi.proceed();
    }
}

接著再來看 AfterReturningAdviceInterceptor#invoke()

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

    private final AfterReturningAdvice advice;
    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    @Nullable
    public Object invoke(MethodInvocation mi) throws Throwable {
        //先執(zhí)行中間的 MethodInvocation研乒,比如 目標方法
        Object retVal = mi.proceed();
        // 再執(zhí)行 AfterReturningAdvice advice的 afterReturning(),這個   
        // afterReturning() 就是我們定義的后置處理的方法體
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }
}

各位看官汹忠,到這里是不是很明了了。簡而言之雹熬,就是遍歷所有的增強器(攔截器)宽菜,有前置增強器就先執(zhí)行它,接著執(zhí)行目標方法竿报,再執(zhí)行后置增強器铅乡。就是辣么竿丹~~

CglibAopProxy 也類似,最終也是調(diào)用 ReflectiveMethodInvocation#proceed()~~

各位看官烈菌,下節(jié)更精彩阵幸,點個贊,年薪百萬不是夢~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芽世,一起剝皮案震驚了整個濱河市挚赊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌捂襟,老刑警劉巖咬腕,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異葬荷,居然都是意外死亡涨共,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門宠漩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來举反,“玉大人,你說我怎么就攤上這事扒吁』鸨牵” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵雕崩,是天一觀的道長魁索。 經(jīng)常有香客問我,道長盼铁,這世上最難降的妖魔是什么粗蔚? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮饶火,結(jié)果婚禮上鹏控,老公的妹妹穿的比我還像新娘致扯。我一直安慰自己,他們只是感情好当辐,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布抖僵。 她就那樣靜靜地躺著,像睡著了一般缘揪。 火紅的嫁衣襯著肌膚如雪耍群。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天找筝,我揣著相機與錄音世吨,去河邊找鬼。 笑死呻征,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的罢浇。 我是一名探鬼主播陆赋,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嚷闭!你這毒婦竟也來了攒岛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤胞锰,失蹤者是張志新(化名)和其女友劉穎灾锯,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗅榕,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡顺饮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凌那。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兼雄。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖帽蝶,靈堂內(nèi)的尸體忽然破棺而出赦肋,到底是詐尸還是另有隱情,我是刑警寧澤励稳,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響犁功,放射性物質(zhì)發(fā)生泄漏晚顷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一扶欣、第九天 我趴在偏房一處隱蔽的房頂上張望鹅巍。 院中可真熱鬧千扶,春花似錦、人聲如沸骆捧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敛苇。三九已至妆绞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枫攀,已是汗流浹背括饶。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留来涨,地道東北人图焰。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像蹦掐,于是被迫代替她去往敵國和親技羔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

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