38--SpringAop代理調(diào)用過程(二)

接前面一章繼續(xù)分析SpringAOP獲取攔截器鏈和攔截器鏈的調(diào)用過程营勤。

1.獲取攔截器鏈
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);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}
/**
 * 獲取攔截器鏈
 */
@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對象,Spring默認(rèn)初始化了MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter和ThrowsAdviceAdapter
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    // 獲取所有增強(qiáng)
    Advisor[] advisors = config.getAdvisors();
    // 創(chuàng)建interceptorList保存返回結(jié)果,這里可以看到new ArrayList指定了集合長度,也是編碼中節(jié)約內(nèi)存開銷的一個(gè)小技巧
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    // 獲取代理類的Class對象
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    Boolean hasIntroductions = null;

    // 循環(huán)所有的增強(qiáng)
    for (Advisor advisor : advisors) {
        // 如果增強(qiáng)是PointcutAdvisor的實(shí)例
        if (advisor instanceof PointcutAdvisor) {
            // Add it conditionally.
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // config.isPreFiltered() -> 返回是否對該代理配置進(jìn)行了預(yù)篩選,以便僅對其進(jìn)行篩選包含適用的增強(qiáng)(匹配此代理的目標(biāo)類)。
            // pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass) -> 當(dāng)前切點(diǎn)匹配的類是否匹配actualClass
            // 以上兩個(gè)條件是在類一級別上做出判斷,如果符合,則接下來對方法級別的再做匹配判斷
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                // 獲取當(dāng)前切點(diǎn)匹配的方法
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                boolean match;
                // 區(qū)分普通的MethodMatcher和IntroductionAwareMethodMatcher,分別調(diào)用不同的匹配方法做出判斷
                // IntroductionAwareMethodMatcher可以作用域引入類型的增強(qiáng),且當(dāng)匹配方法不包含引用增強(qiáng)時(shí),可以提升匹配效率
                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) {
                    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) {
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
            }
        }
        // 如果增強(qiáng)是IntroductionAdvisor實(shí)例
        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));
            }
        }
        // 其他類型
        else {
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
        }
    }

    return interceptorList;
}

該段代碼比較關(guān)鍵的點(diǎn):

// 如果匹配,獲取方法攔截器
if (match) {
    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) {
            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
        }
    }
    else {
        interceptorList.addAll(Arrays.asList(interceptors));
    }
}

通過上面的代碼可以發(fā)現(xiàn)Spring最終還是要把增強(qiáng)(切面)轉(zhuǎn)換為方法攔截器撵幽,來看其具體的實(shí)現(xiàn):

/**
 * 獲取方法連接器
 */
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    // 1.如果增強(qiáng)是MethodInterceptor類型直接添加
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    // 2.循環(huán)增強(qiáng)適配器,并判斷是否支持
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    // 3.返回結(jié)果
    return interceptors.toArray(new MethodInterceptor[0]);
}

通過這段代碼痴荐,如果增強(qiáng)是MethodInterceptor的實(shí)例烘豌,直接加入結(jié)果中华蜒;那么哪些增強(qiáng)是MethodInterceptor類型呢辙纬?先不用著急,繼續(xù)看下面的處理叭喜,下面的代碼里出現(xiàn)了一個(gè)增強(qiáng)適配器贺拣,我們來看一下其中都包含了哪些適配器類型:

private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

DefaultAdvisorAdapterRegistry通過構(gòu)造函數(shù)對增強(qiáng)適配器進(jìn)行了初始化,包含了MethodBeforeAdviceAdapter前置增強(qiáng)適配器域滥、AfterReturningAdviceAdapter后置返回增強(qiáng)適配器纵柿、ThrowsAdviceAdapter后置異常增強(qiáng)適配器蜈抓。那么從這里我們也可以推斷出启绰,除了這幾種增強(qiáng)適配器對應(yīng)的增強(qiáng)類型之外,其他的都是MethodInterceptor類型沟使。

接下來看下這三種適配器都做了哪些工作:

  • MethodBeforeAdviceAdapter
/**
 * 增強(qiáng)適配器
 * 這個(gè)方法在獲取攔截器鏈的時(shí)候調(diào)用,從這里也可以看出,Spring中的advisor(增強(qiáng)/切面)
 * 最終還是被轉(zhuǎn)換為MethodInterceptor對象
 *
 * AdvisorAdapter的實(shí)現(xiàn)類有AfterReturningAdviceAdapter,MethodBeforeAdviceAdapter,ThrowsAdviceAdapter三個(gè)
 *
 * AfterReturningAdviceAdapter -> new AfterReturningAdviceInterceptor(advice) -> 后置返回增強(qiáng)
 * MethodBeforeAdviceAdapter -> new MethodBeforeAdviceInterceptor(advice) -> 前置增強(qiáng)
 * ThrowsAdviceAdapter -> new ThrowsAdviceInterceptor(advisor.getAdvice()) -> 該適配器有些特殊...看源碼吧
 */
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
    MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    return new MethodBeforeAdviceInterceptor(advice);
}
  • AfterReturningAdviceAdapter
public MethodInterceptor getInterceptor(Advisor advisor) {
    AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
    return new AfterReturningAdviceInterceptor(advice);
}
  • ThrowsAdviceAdapter
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
    return new ThrowsAdviceInterceptor(advisor.getAdvice());
}

看到這里大家一定有所了解了委可,Spring中的增強(qiáng),最終還是會轉(zhuǎn)換為方法攔截器調(diào)用腊嗡。

3. 攔截器鏈調(diào)動過程
/**
 * 調(diào)用攔截器鏈
 *
 * currentInterceptorIndex維護(hù)了一個(gè)計(jì)數(shù)器着倾,該計(jì)數(shù)器從-1開始,當(dāng)計(jì)數(shù)器值等于攔截方法長度減一時(shí)燕少,
 * 表名所有的增強(qiáng)方法已經(jīng)被調(diào)用(但是不一定被真正執(zhí)行)卡者,那么此時(shí)調(diào)用連接點(diǎn)的方法,針對本例:即sayHello方法
 */
@Override
@Nullable
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);

    // 動態(tài)匹配增強(qiáng)
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 匹配成功則執(zhí)行
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        // 匹配失敗則跳過并執(zhí)行下一個(gè)攔截器
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    // 靜態(tài)增強(qiáng)
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        // System.out.println(interceptorOrInterceptionAdvice.getClass());
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

這段代碼看似簡單客们,但是如果真的debug進(jìn)去崇决,方法棧還是比較深刻的,前面介紹過在獲取到合適的增強(qiáng)集合之后底挫,首先在其首位加入了ExposeInvocationInterceptor攔截器恒傻,然后對增強(qiáng)集合進(jìn)行了排序(當(dāng)然ExposeInvocationInterceptor依然會在首位),那么接下來第一個(gè)攔截器調(diào)用就是ExposeInvocationInterceptor了建邓。

  1. ExposeInvocationInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    MethodInvocation oldInvocation = invocation.get();
    invocation.set(mi);
    try {
        // 繼續(xù)攔截器鏈調(diào)用
        return mi.proceed();
    }
    finally {
        invocation.set(oldInvocation);
    }
}
  1. AspectJAfterThrowingAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 繼續(xù)攔截器鏈調(diào)用
        return mi.proceed();
    }
    catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}
  1. AspectJAfterThrowingAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 繼續(xù)攔截器鏈調(diào)用
        return mi.proceed();
    }
    catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}
  1. AfterReturningAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    // 繼續(xù)攔截器鏈調(diào)用
    Object retVal = mi.proceed();
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}
  1. AspectJAfterAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 繼續(xù)攔截器鏈調(diào)用
        return mi.proceed();
    }
    finally {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}
  1. AspectJAroundAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    }
    ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
    JoinPointMatch jpm = getJoinPointMatch(pmi);
    return invokeAdviceMethod(pjp, jpm, null, null);
}

在這里終于看到了方法的調(diào)用:

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterCount() == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        // TODO AopUtils.invokeJoinpointUsingReflection
        // 調(diào)用代理方法
        return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    }
    catch (IllegalArgumentException ex) {
        throw new AopInvocationException("Mismatch on arguments to advice method [" +
                this.aspectJAdviceMethod + "]; pointcut expression [" +
                this.pointcut.getPointcutExpression() + "]", ex);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
  }
public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

代碼終于執(zhí)行到了Method類的invoke方法盈厘,接下來就可以調(diào)用我們的增強(qiáng)方法了:

  1. MethodBeforeAdviceInterceptor
@Around("test()")
public Object aroundTest(ProceedingJoinPoint p) throws Throwable {
    System.out.println("==環(huán)繞增強(qiáng)開始");
    // 繼續(xù)攔截器鏈調(diào)用
    Object o = p.proceed();
    System.out.println("==環(huán)繞增強(qiáng)結(jié)束");
    return o;
}

執(zhí)行完之后,我們發(fā)現(xiàn)前置增強(qiáng)還沒有被調(diào)用進(jìn)來官边,繼續(xù):

public Object invoke(MethodInvocation mi) throws Throwable {
    // 調(diào)用前置增強(qiáng)
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}
  1. 目標(biāo)方法
    執(zhí)行完前置增強(qiáng)之后沸手,再次進(jìn)入到proceed方法,這時(shí)候注簿,計(jì)數(shù)器已經(jīng)滿足條件了契吉,執(zhí)行目標(biāo)方法調(diào)用:
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    return invokeJoinpoint();
}

待執(zhí)行完目標(biāo)方法調(diào)用后,再將之前壓入方法棧的那些增強(qiáng)方法依次出棧并調(diào)用滩援。這一段的代碼調(diào)用用文字表述挺困難的栅隐,大家自己跟蹤代碼吧。。租悄。

到這里攔截器鏈的調(diào)用過程分析就結(jié)束了谨究,這里分析的不是那么明確,還是多跟蹤代碼吧泣棋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胶哲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子潭辈,更是在濱河造成了極大的恐慌鸯屿,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件把敢,死亡現(xiàn)場離奇詭異寄摆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)修赞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門婶恼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柏副,你說我怎么就攤上這事勾邦。” “怎么了割择?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵眷篇,是天一觀的道長。 經(jīng)常有香客問我荔泳,道長蕉饼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任换可,我火速辦了婚禮椎椰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沾鳄。我一直安慰自己慨飘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布译荞。 她就那樣靜靜地躺著瓤的,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吞歼。 梳的紋絲不亂的頭發(fā)上圈膏,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音篙骡,去河邊找鬼稽坤。 笑死丈甸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尿褪。 我是一名探鬼主播睦擂,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼杖玲!你這毒婦竟也來了顿仇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤摆马,失蹤者是張志新(化名)和其女友劉穎臼闻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體囤采,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡述呐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斑唬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片市埋。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡黎泣,死狀恐怖恕刘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抒倚,我是刑警寧澤褐着,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站托呕,受9級特大地震影響含蓉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜项郊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一馅扣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧着降,春花似錦差油、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至交掏,卻和暖如春妆偏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盅弛。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工钱骂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叔锐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓见秽,卻偏偏與公主長得像掌腰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子张吉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

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