深入理解Spring 之 源碼剖析AOP(注解方式)

上一篇文章我們從XML 配置文件的方式剖析了AOP的源碼,我們也說(shuō)了,雖然現(xiàn)在用XML配置的幾乎沒有了,但作為學(xué)習(xí)的例子,XML配置仍然是我們理解Spring AOP 的一個(gè)絕好的樣本,但作為一個(gè)由追求的程序員,我們天天使用的注解方式的AOP 肯定也是要去看看到底是如何實(shí)現(xiàn)的?,F(xiàn)在有了之前閱讀 XML 配置的源碼的基礎(chǔ),今天我們來(lái)閱讀注解方式的源碼也變得輕松起來(lái)。

還記得我們之前說(shuō)過(guò),XML 配置的AOP是使用 ProxyFactoryBean ,實(shí)現(xiàn)了 FactoryBean的接口,而FactoryBean是Spring特意留給開發(fā)者們擴(kuò)展的接口,而Spring 留給開發(fā)者們不止一個(gè)擴(kuò)展接口,比如 BeanPostProcess 接口,實(shí)現(xiàn)著接口就可以在每個(gè)Bean的生成前后做一些增強(qiáng)或自定義(具體Spring 留給我們有哪些擴(kuò)展接口,樓主有機(jī)會(huì)將會(huì)再寫一篇文章解析)。

接下來(lái)就要好好講講我們上篇文章漏講的接口設(shè)計(jì)。這是我們理解 AOP 的基礎(chǔ)。

先從 ProxyFactoryBean 講起,這個(gè)熟悉的哥們。

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

1.1 ProxyCreatorSupport 類結(jié)構(gòu)圖

它繼承了 ProxyCreatorSupport 這個(gè)類,這個(gè)類很重要,我們看看該類結(jié)構(gòu)

該類有2個(gè)重要的方法,分別是獲取代理工廠,創(chuàng)建代理。那么代理工廠是什么呢?AopProxyFactory ,是個(gè)接口,只定義了一個(gè)方法:AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException, 并且該接口目前只有一個(gè)默認(rèn)實(shí)現(xiàn)類:DefaultAopProxyFactory,該類主要重寫了接口方法 createAopProxy, 內(nèi)部邏輯是根據(jù)給定的類配置來(lái)創(chuàng)建不同的代理 AopProxy,那么 AopProxy 是什么呢?就是真正創(chuàng)建代理的工廠,他是一個(gè)接口,有3個(gè)實(shí)現(xiàn):

1.2 AopProxy 接口

1.3 AopProxy 繼承結(jié)構(gòu)

從名字上可以看出來(lái),一個(gè)是 JDK 動(dòng)態(tài)代理,一個(gè)是 Cglib 代理,ObjenesisCglibAopProxy 擴(kuò)展了它的父類 CglibAopProxy,在 DefaultAopProxyFactory 的實(shí)現(xiàn)里,使用的就是 ObjenesisCglibAopProxy 來(lái)實(shí)現(xiàn) Cglib 的代理。他們分別實(shí)現(xiàn)了自己的getProxy 方法用以創(chuàng)建代理。我們看看這兩個(gè)類的繼承結(jié)構(gòu):

1.4 JdkDynamicAopProxy 繼承結(jié)構(gòu)

1.5 CglibAopProxy 繼承結(jié)構(gòu)沒什么,主要是眾多內(nèi)部類

可以看到喲非常多的內(nèi)部類,這些內(nèi)部類是為了實(shí)現(xiàn)了 Cglib 的各種回調(diào)而實(shí)現(xiàn)的。主要實(shí)現(xiàn)了 MethodInterceptor 接口,
Callback 接口,Joinpoint 接口,Invocation 接口等待,總之是實(shí)現(xiàn)了Spring 的 cglib 模塊的各種接口。

說(shuō)了那么多,我們回來(lái)看看 ProxyCreatorSupport ,下面是 ProxyCreatorSupport 的繼承結(jié)構(gòu)

2. ProxyCreatorSupport 類繼承圖

該類有3個(gè)子類, 其中就有一個(gè)我們熟悉的 ProxyFactoryBean,該類實(shí)現(xiàn)了我們熟悉的 FactoryBean,還有一個(gè)可以獲取容器內(nèi)Bean的 BeanFactoyAware 接口,第二個(gè)是陌生的 AspectJProxyFactory 類,該類是用于集成 Spring 和 AspectJ 的。而最后一個(gè)類ProxyFactory 就是我們今天的主角,Spring 的類注釋說(shuō):用于編程使用的AOP代理,而不是在bean工廠中通過(guò)聲明式設(shè)置。這個(gè)類提供了一種簡(jiǎn)單的方法,可以在定制的用戶代碼中獲取和配置AOP代理實(shí)例,大概意思就是通過(guò)編程的方式獲取Bean 代理吧,而不是通過(guò)配置文件的方式。我們今天就可以見識(shí)到。

3. AnnotationAwareAspectJAutoProxyCreator 類

這個(gè)類的名字很長(zhǎng),為什么要說(shuō)這個(gè)類呢?還記得我們剛開始說(shuō)的 BeanPostProcessor 擴(kuò)展接口嗎?我們說(shuō)該接口是spring 留給開發(fā)人員自定義增強(qiáng)bean的接口。而該類則實(shí)現(xiàn)了該接口,看名字也知道,該類是根據(jù)注解自動(dòng)創(chuàng)建代理的創(chuàng)建者類。我們看看他的類圖:

可以看到,最底層的該類實(shí)現(xiàn)了 BeanPostProcessor 接口,可以在每個(gè)bena生成前后做操作。該類由 Rod Johnson 編寫,注釋上是這么說(shuō)的:任何AspectJ注釋的類都將自動(dòng)被識(shí)別,它們也會(huì)被識(shí)別。和我們預(yù)想的一致。

我們知道了 AnnotationAwareAspectJAutoProxyCreator 是根據(jù)注解自動(dòng)創(chuàng)建代理,而該類也算是 ProxyBean 的代理,那么,和它一樣繼承抽象父類的其他幾個(gè)類的作用是什么呢?來(lái)都來(lái)了,就看看吧!我們看看類圖:

可以看到該類由4個(gè)實(shí)現(xiàn):他們實(shí)現(xiàn)了不同的創(chuàng)建代理的方式:

  1. 匹配Bean的名稱自動(dòng)創(chuàng)建匹配到的Bean的代理,實(shí)現(xiàn)類BeanNameAutoProxyCreator
  2. 根據(jù)Bean中的AspectJ注解自動(dòng)創(chuàng)建代理,實(shí)現(xiàn)類AnnotationAwareAspectJAutoProxyCreator,也就是我們今天說(shuō)的注解類。
  3. 根據(jù)Advisor的匹配機(jī)制自動(dòng)創(chuàng)建代理,會(huì)對(duì)容器中所有的Advisor進(jìn)行掃描,自動(dòng)將這些切面應(yīng)用到匹配的Bean中,實(shí)現(xiàn)類DefaultAdvisorAutoProxyCreator
  4. InfrastructureAdvisorAutoProxyCreator,該類只在 AopConfigUtils 中的靜態(tài)塊用到,該類的注釋:自動(dòng)代理創(chuàng)建者只考慮基礎(chǔ)設(shè)施顧問(wèn)bean,忽略任何應(yīng)用程序定義的顧問(wèn)。意思應(yīng)該是只是Sprnig的基礎(chǔ)代理,開發(fā)者的應(yīng)用會(huì)忽略。有知道的同學(xué)可以告訴我。

加上我們的ProxyFactoryBean,一共5種實(shí)現(xiàn)方法。從這里可以看出Spring 對(duì)于擴(kuò)展的軟件設(shè)計(jì)是多么優(yōu)秀。

那么我們就來(lái)看看 AnnotationAwareAspectJAutoProxyCreator 是如何創(chuàng)建代理的。我們說(shuō) BeanPostProcessor 是Spring 留給我們擴(kuò)展的接口,那么他是如何定義的呢?我們看看該接口:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

該接口定義了兩個(gè)方法,一個(gè)是在bean初始化之前執(zhí)行,一個(gè)是在bean初始化之后執(zhí)行。也就是說(shuō),開發(fā)者可以在這兩個(gè)方法中做一些有趣的事情。我們看看 AnnotationAwareAspectJAutoProxyCreator 是如何實(shí)現(xiàn)該方法的。實(shí)際上 AnnotationAwareAspectJAutoProxyCreator 的抽象父類已經(jīng)實(shí)現(xiàn)了該方法,我們看看是如何實(shí)現(xiàn)的:

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * Create a proxy with the configured interceptors if the bean is
     * identified as one to proxy by the subclass.
     * @see #getAdvicesAndAdvisorsForBean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

before 方法直接返回了bean,并沒有做什么增強(qiáng)操作,重點(diǎn)在after方法,我們可以看該方法的注釋:如果bean是由子類標(biāo)識(shí)的,那么就創(chuàng)建一個(gè)配置的攔截器的代理。Spring 就是在這里創(chuàng)建了代理,我們進(jìn)入關(guān)鍵方法 wrapIfNecessary 看看。該方法用來(lái)包裝給定的Bean。

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

我們已將可以看到一個(gè)關(guān)鍵一行代碼:Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)),創(chuàng)建一個(gè)代理,該方法攜帶了bean的Class對(duì)象,benaName, 通知器數(shù)組,還有一個(gè)包裝過(guò)的單例Bean,我們看看該方法實(shí)現(xiàn)(該方法對(duì)于我們來(lái)說(shuō)已經(jīng)到終點(diǎn)了),

    protected Object createProxy(
            Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
        }

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);

        if (!proxyFactory.isProxyTargetClass()) {
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }

        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);

        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }

        return proxyFactory.getProxy(getProxyClassLoader());
    }

我們能夠看到其中有一行讓我們激動(dòng)的代碼,就是 ProxyFactory proxyFactory = new ProxyFactory(),創(chuàng)建了一個(gè)ProxyFactory 對(duì)象,我們剛剛說(shuō)過(guò),該對(duì)象和ProxyFactoryBean一樣,繼承了 ProxyCreatorSupport, 因此也就和 ProxyFactoryBean 一樣擁有了 getProxy 方法,到這里,我們的一切都豁然開朗。當(dāng)然上面幾行代碼是設(shè)置了通知器鏈。我們先按下不表。

ProxyFactoryBean 擴(kuò)展了 FactoryBean 接口, AnnotationAwareAspectJAutoProxyCreator 擴(kuò)展了 BenaPostProcessor 了接口,其目的都是在Bean生成的時(shí)候做增強(qiáng)操作,Spring 通過(guò)這兩種方式,完成了兩種不同的代理生成方式,但最終都是繼承了 ProxyCreatorSupport 類,該類才是生成代理的核心類。

我們可以看看XML 配置方式和 注解方式的方法堆棧調(diào)用圖,從中我們可以看出一些端倪:

XML 配置方式堆棧圖

注解配置方法堆棧圖

我們可以看到兩者在AbstractBeanFactory 的 doGetBean 方法開始分道揚(yáng)鑣,走向了不同的邏輯,那么我們看看到底哪里不同,直接看代碼:我們看XML配置堆棧,在258行:

從這里進(jìn)入,該方法做了些什么呢?為什么讓他們走向不同的路線?我們看看該方法:

該方法有個(gè)重要的判斷:是否是 FactoryBean 的子類型,很明顯,我們的XML配置的ProxyFacoroyBean 返回 false,而注解方式的Bean則返回 false,ProxyFacoroyBean 會(huì)一直向下走,直到創(chuàng)建代理,而注解方式則會(huì)直接返回。走到302行:

注解方式會(huì)執(zhí)行 getSingleton 方法,最后觸發(fā) createBean 回調(diào)方法,完成創(chuàng)建代理的過(guò)程。

到此為止,現(xiàn)在我們知道了到 XML 配置方式的 AOP 和注解的方式AOP 的生成區(qū)別。我們可以開始總結(jié)了。

4. 總結(jié)

首先,通過(guò)分析源碼我們知道注解方式和 XML 配置方式的底層實(shí)現(xiàn)都是一樣的,都是通過(guò)繼承 ProxyCreatorSupport 來(lái)實(shí)現(xiàn)的,不同的通過(guò)擴(kuò)展不同的 Spring 提供的接口,XML 擴(kuò)展的是FactoryBean 接口, 而注解方式擴(kuò)展的是 BenaPostProcessor 接口,通過(guò)Spring 的擴(kuò)展接口,能夠?qū)μ囟ǖ腂ean進(jìn)行增強(qiáng)。而 AOP 正式通過(guò)這種方式實(shí)現(xiàn)的。這也提醒了我們,我們也可以通過(guò)擴(kuò)展 Spring 的某些接口來(lái)增強(qiáng)我們需要的 Bean 的某些功能。當(dāng)然,篇幅有限,我們這篇文章只是了解了XML 配置方式和注解方式創(chuàng)建代理的區(qū)別,關(guān)于如何 @Aspect 和 @Around 的底層實(shí)現(xiàn),還有通知器的底層實(shí)現(xiàn),我們還沒有分析,但我們隱隱的感覺到,其實(shí)萬(wàn)變不離其宗,底層的也是通過(guò)擴(kuò)展 advice 和 pointcut 接口來(lái)實(shí)現(xiàn)的。 我們將會(huì)在后面的文章繼續(xù)分析 AOP 是如何編織通知的。

good luck ?。。?!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,715評(píng)論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,290評(píng)論 6 342
  • Spring 的兩大核心,一是IOC,我們之前已經(jīng)學(xué)習(xí)過(guò),并且已經(jīng)自己動(dòng)手實(shí)現(xiàn)了一個(gè),而令一個(gè)則是大名鼎鼎的 AO...
    莫那一魯?shù)?/span>閱讀 1,380評(píng)論 0 6
  • 什么是Spring Spring是一個(gè)開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,786評(píng)論 1 133
  • 判斷當(dāng)前文本是否包含中文 issue:此方法只能判斷部分CJK字符(CJK統(tǒng)一漢字) issue:完美判斷是否包含...
    湯瓜瓜閱讀 319評(píng)論 0 0

友情鏈接更多精彩內(nèi)容