2.SpringAop之AdvisedSupport

1.類繼承結(jié)構(gòu)

2.解析

AdvisedSupport繼承ProxyConfig,自身提供了以下功能

  1. 配置當(dāng)前代理的Adivsiors
  2. 配置當(dāng)前代理的目標(biāo)對象
  3. 配置當(dāng)前代理的接口
  4. 提供getInterceptorsAndDynamicInterceptionAdvice方法用來獲取對應(yīng)代理方法對應(yīng)有效的攔截器鏈

AdvisedSupport本身不會提供創(chuàng)建代理的任何方法,專注于生成攔截器鏈

我們來探究下第4點中的攔截器是什么東西介蛉,以及getInterceptorsAndDynamicInterceptionAdvice的源碼實現(xiàn)

先看下攔截器(MethodInterceptor)的接口定義

public interface MethodInterceptor extends Interceptor {
    
    Object invoke(MethodInvocation invocation) throws Throwable;
}

攔截器的作用是封裝在目標(biāo)方法前后執(zhí)行相應(yīng)的邏輯泛粹,在Aop中的前置,后置器仗,環(huán)繞等通知都有對應(yīng)攔截器實現(xiàn)

再來看下MethodInvocation的定義

image

MethodInvocation中proceed方法比較重要

/**
* Proceeds to the next interceptor in the chain.
*
* <p>The implementation and the semantics of this method depends
* on the actual joinpoint type (see the children interfaces).
*
* @return see the children interfaces' proceed definition.
*
* @throws Throwable if the joinpoint throws an exception. */
Object proceed() throws Throwable;

proceed()方法封裝了調(diào)用下一個攔截器的邏輯,如果攔截器執(zhí)行完畢童番,那么就執(zhí)行被代理方法
目前在spring框架里面MethodInvocation接口的實現(xiàn)類只有一個精钮,就是ReflectiveMethodInvocation
看下它的proceed方法實現(xiàn)

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.
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.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.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

這里會依次執(zhí)行攔截器,最后執(zhí)行被代理的方法
其中InterceptorAndDynamicMethodMatcher為動態(tài)攔截器剃斧,根據(jù)運行時參數(shù)來決定攔截器是否生效
有動態(tài)攔截器當(dāng)然也有靜態(tài)攔截器轨香,靜態(tài)攔截器一般通過包名,類名幼东,方法名 臂容,參數(shù)來確定靜態(tài)攔截器是否對代理方法生效

proceed方法中的interceptorsAndDynamicMethodMatchers攔截器鏈就是由AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice來獲取,接下來看下獲取攔截器鏈的具體實現(xiàn)

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, 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;
}

這個方法對需要獲取的攔截器鏈做了緩存根蟹,實際獲取邏輯委托給DefaultAdvisorChainFactory來實現(xiàn)

public interface AdvisorChainFactory {

    /**
     * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
     * for the given advisor chain configuration.
     * @param config the AOP configuration in the form of an Advised object
     * @param method the proxied method
     * @param targetClass the target class (may be {@code null} to indicate a proxy without
     * target object, in which case the method's declaring class is the next best option)
     * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
     */
    List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass);

}

在DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass)方法中脓杉,遍歷AdviseSupport里面配置的所有advisors,并且執(zhí)行PointCut中MethodMatcher的matches方法來決定對應(yīng)Advice是否生效娜亿,最后得到代理方法的攔截器鏈
具體代碼邏輯如下

AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
    if (advisor instanceof PointcutAdvisor) {
        // Add it conditionally.
        PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
        if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
            MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
            MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
            if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                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));
                }
            }
        }
    }
    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));
    }
}

注意到將Advisor轉(zhuǎn)換為Interceptor攔截器使用了registry.getInterceptors(advisor)丽已,轉(zhuǎn)換的邏輯委托給了AdvisorAdapterRegistry實現(xiàn),注意下轉(zhuǎn)換后得到的是數(shù)組买决,因為Advisor可配置多個Advice

AdvisorAdapterRegistry接口定義如下

public interface AdvisorAdapterRegistry {

    Advisor wrap(Object advice) throws UnknownAdviceTypeException;

    MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException;

    void registerAdvisorAdapter(AdvisorAdapter adapter);
}

其中g(shù)etInterceptors方法實現(xiàn)如下

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
    Advice advice = advisor.getAdvice();
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}

在這個方法里面主要通過AdvisorAdapter 來把Advisor里面的Adivce(通知)轉(zhuǎn)換為對應(yīng)的攔截器MethodInterceptor

AdvisorAdapter接口定義如下

public interface AdvisorAdapter {

    boolean supportsAdvice(Advice advice);

    MethodInterceptor getInterceptor(Advisor advisor);

}

從接口推斷出沛婴,每種類型Advisor都會有一個AdvisorAdapter接口實現(xiàn)對應(yīng)
在DefaultAdvisorAdapterRegistry中默認(rèn)注冊了3個AdvisorAdapter

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

注意到并沒有環(huán)繞通知,因為環(huán)繞通知直接采用MethodInteceptor來實現(xiàn)
在getInterceptors方法里面可以看到

if (advice instanceof MethodInterceptor) {
    interceptors.add((MethodInterceptor) advice);
}

這句代碼就是為了給環(huán)繞通知做處理

看下AdvisorAdapter和對應(yīng)攔截器的具體實現(xiàn)能讓我們理解更加深刻督赤,我們來看下MethodBeforeAdviceAdapter和MethodBeforeAdviceInterceptor

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

    private MethodBeforeAdvice advice;


    /**
     * Create a new MethodBeforeAdviceInterceptor for the given advice.
     * @param advice the MethodBeforeAdvice to wrap
     */
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }
}

轉(zhuǎn)換攔截器的作用主要是形成鏈?zhǔn)秸{(diào)用嘁灯,在攔截器的invoke方法都會觸發(fā)調(diào)用下一個攔截器

總結(jié)

這個類的主要作用是將Advisor轉(zhuǎn)換為有效攔截器鏈,接下來我們只要關(guān)注怎么生成代理對象躲舌,以及在代理對象中怎么調(diào)用攔截器鏈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末丑婿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌羹奉,老刑警劉巖秒旋,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诀拭,居然都是意外死亡迁筛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門耕挨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來细卧,“玉大人,你說我怎么就攤上這事筒占√懊恚” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵翰苫,是天一觀的道長止邮。 經(jīng)常有香客問我,道長奏窑,這世上最難降的妖魔是什么农尖? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮良哲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘助隧。我一直安慰自己筑凫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布并村。 她就那樣靜靜地躺著巍实,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哩牍。 梳的紋絲不亂的頭發(fā)上棚潦,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音膝昆,去河邊找鬼丸边。 笑死,一個胖子當(dāng)著我的面吹牛荚孵,可吹牛的內(nèi)容都是我干的妹窖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼收叶,長吁一口氣:“原來是場噩夢啊……” “哼骄呼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蜓萄,失蹤者是張志新(化名)和其女友劉穎隅茎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嫉沽,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡辟犀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了耻蛇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踪蹬。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖臣咖,靈堂內(nèi)的尸體忽然破棺而出跃捣,到底是詐尸還是另有隱情,我是刑警寧澤夺蛇,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布疚漆,位于F島的核電站,受9級特大地震影響刁赦,放射性物質(zhì)發(fā)生泄漏娶聘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一甚脉、第九天 我趴在偏房一處隱蔽的房頂上張望丸升。 院中可真熱鬧,春花似錦牺氨、人聲如沸狡耻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夷狰。三九已至,卻和暖如春郊霎,著一層夾襖步出監(jiān)牢的瞬間沼头,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工书劝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留进倍,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓庄撮,卻偏偏與公主長得像背捌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子洞斯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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