Spring源碼解析(十二)-配置通知器和Advice的通知實現(xiàn)

1.配置通知器

其實如何配置攔截器的問題誊酌,可以轉(zhuǎn)化為攔截器元素是從哪來的部凑,又在哪邊配置的問題”套牵看過上章Aop攔截器調(diào)用解析的博文的朋友肯定有印象存在著一行代碼

// 獲得該方法定義好的連接器鏈.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

這里的advised是AdvisedSupport對象涂邀,我們來看一下它的具體實現(xiàn)

   //這里運用緩存,存入了Map<MethodCacheKey, List<Object>>
   //key:Method ; value: advisor chain List
   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的getInterceptorsAndDynamicInterceptionAdvice方法
    //獲取通知
    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, Class<?> targetClass) {

        //根據(jù)AOP中配置創(chuàng)建通知器鏈的集合.
        List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
        //獲取目標(biāo)類的class對象
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        //判斷Advisors是否符合配置要求
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        //獲取AdvisorAdapterRegistry對象
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        //遍歷advisors
        for (Advisor advisor : config.getAdvisors()) {
            //PointcutAdvisor
            if (advisor instanceof PointcutAdvisor) {
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                //AOP配置對通知已經(jīng)過濾或者當(dāng)前切入點的類過濾器匹配目標(biāo)類
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    //獲取advisor中的方法攔截器列表
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    //獲取advisor切點的方法匹配器
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    //目標(biāo)類方法匹配切點
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                       //方法匹配器是運行時動態(tài)匹配的
                        if (mm.isRuntime()) {
                            //將方法攔截器和方法匹配器封裝后添加到返回的通知器集合中.
                            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));
            }
        }

        return interceptorList;
    }

    private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
        for (int i = 0; i < config.getAdvisors().length; i++) {
            Advisor advisor = config.getAdvisors()[i];
            //通知器是引入通知器
            if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                //是否匹配目標(biāo)類
                if (ia.getClassFilter().matches(actualClass)) {
                    return true;
                }
            }
        }
        return false;
    }

在ProxyFactoryBean的getObject方法中對advisor進行初始化時箱锐,從XML配置中獲取了Advisor通知器比勉。在初始化Advisor中,可以看到對IOC容器的一個getBean回調(diào)來得到配置好的Advisor通知器驹止。那么ProxyFactoryBean是如何獲得IOC容器浩聋,然后通過回調(diào)來獲得Advisor的呢?對于IOC容器的使用幢哨,如果需要回調(diào)容器赡勘,前提是當(dāng)前的Bean實現(xiàn)BeanFactoryAware接口嫂便,并實現(xiàn)setBeanFactory方法捞镰,同時設(shè)置一個屬性來持有IOC容器,就可以在Bean中取得IOC容器并進行回調(diào)了,而ProxyFactoryBean就實現(xiàn)了這個接口岸售。ProxyFactoryBean給出通知器的名字(這些名字在XML中interceptorNames的list中)践樱,在IOC對FactoryBean進行依賴注入的時候,會直接注入到FactoryBean的interceptorNames的屬性中凸丸。完成這個過程以后拷邢,ProxyFactoryBean就獲得了配置的通知器。

2.Advice的通知實現(xiàn)

首先我們來看下如何獲取通知器的通知屎慢,我們在解析如何獲取配置通知器的時候出現(xiàn)過如下這行代碼

AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

因為AdvisorAdapterRegistry是一個接口瞭稼,我們來看一下它的默認實現(xiàn)類DefaultAdvisorAdapterRegistry


 public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
    //通知器適配器集合
    private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);


    /**
     * 構(gòu)造方法,只有3種通知器適配器.
     */
    public DefaultAdvisorAdapterRegistry() {
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }

    //將通知封裝為通知器
    @Override
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            //檢查符合的advice.
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }

    //獲得通知器的通知 
    @Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
        //獲取advice通知
        Advice advice = advisor.getAdvice();
        //如果通知是MethodInterceptor類型,則加入interceptors
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        //對通知進行適配腻惠,從適配器(MethodBeforeAdviceAdapter环肘,AfterReturningAdviceAdapter,ThrowsAdviceAdapter)中取出封裝好AOP編織功能的攔截器
        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()]);
    }

    //注冊通知適配器
    @Override
    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }

}

我們以MethodBeforeAdviceAdapter為例集灌,看一下MethodBeforeAdviceAdapter的具體實現(xiàn)

 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();
    }

}

如代碼所示悔雹,MethodBeforeAdviceInterceptor 完成的是對MethodBeforeAdvice 通知的封裝,可以在MethodBeforeAdviceInterceptor 設(shè)計的invoke回調(diào)方法中欣喧,看到首先觸發(fā)了advice的before方法腌零,然后才是MethodInvocation的proceed方法調(diào)用。而proceed方法已經(jīng)在第十一章中分析完畢唆阿。以上則是Spring AOP的所有內(nèi)容益涧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市驯鳖,隨后出現(xiàn)的幾起案子饰躲,更是在濱河造成了極大的恐慌,老刑警劉巖臼隔,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘹裂,死亡現(xiàn)場離奇詭異,居然都是意外死亡摔握,警方通過查閱死者的電腦和手機寄狼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氨淌,“玉大人泊愧,你說我怎么就攤上這事∈⒄” “怎么了删咱?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長豪筝。 經(jīng)常有香客問我痰滋,道長摘能,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任敲街,我火速辦了婚禮团搞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘多艇。我一直安慰自己逻恐,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布峻黍。 她就那樣靜靜地躺著复隆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姆涩。 梳的紋絲不亂的頭發(fā)上昏名,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音阵面,去河邊找鬼轻局。 笑死,一個胖子當(dāng)著我的面吹牛样刷,可吹牛的內(nèi)容都是我干的仑扑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼置鼻,長吁一口氣:“原來是場噩夢啊……” “哼镇饮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起箕母,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤储藐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嘶是,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钙勃,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年聂喇,在試婚紗的時候發(fā)現(xiàn)自己被綠了辖源。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡希太,死狀恐怖克饶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情誊辉,我是刑警寧澤矾湃,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站堕澄,受9級特大地震影響邀跃,放射性物質(zhì)發(fā)生泄漏霉咨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一坞嘀、第九天 我趴在偏房一處隱蔽的房頂上張望躯护。 院中可真熱鬧惊来,春花似錦丽涩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至枉证,卻和暖如春矮男,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背室谚。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工毡鉴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秒赤。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓猪瞬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親入篮。 傳聞我的和親對象是個殘疾皇子陈瘦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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