深入理解Spring 之 源碼剖析 SpringBoot Aop 切面編織過程和代理執(zhí)行過程

源碼:



# 前言

前兩篇文章我們分析了AOP的原理,知道了AOP的核心就是代理唁盏,通知器,切點(diǎn)检眯。我們也知道了XML配置方式和注解方式的底層實(shí)現(xiàn)都是相同的厘擂,都是通過 ProxyCreatorSupport 創(chuàng)建代理,但是XML是通過 ProxyFactoryBean 來實(shí)現(xiàn)的锰瘸,而 注解是通過 BeanPostProcessor 來實(shí)現(xiàn)創(chuàng)建代理的邏輯驴党。

我們上篇講解注解方式的文章,剖析了 AnnotationAwareAspectJAutoProxyCreator 這個類获茬,這個類就是根據(jù)注解創(chuàng)建代理的默認(rèn)類。那么到底該類在Spring 中是如何運(yùn)行的倔既?基于注解的 AOP 在現(xiàn)在流行的 SpringBoot 中是如何設(shè)計實(shí)現(xiàn)的恕曲?今天樓主就要以一個簡單的demo來 debug 一次源碼,徹底了解 SpringBoot 下的AOP 是如何運(yùn)作的渤涌。

我們將分為幾個方面來講:

  1. AnnotationAwareAspectJAutoProxyCreator 后置處理器注冊過程佩谣。
  2. 標(biāo)注 @Aspect 注解的 bean 的后置處理器的處理過程
  3. 創(chuàng)建代理的過程
  4. 目標(biāo)對象方法調(diào)用的過程

1. AnnotationAwareAspectJAutoProxyCreator 后置處理器注冊過程

1.1 后置處理器繼承圖譜

我們先回顧一下該類的繼承圖譜 ,看圖中紅框部分实蓬,可以看到該類間接實(shí)現(xiàn)了 BeanPostProcessor 接口茸俭,而且也實(shí)現(xiàn)了 InstantiationAwareBeanPostProcessor 接口吊履,InstantiationAwareBeanPostProcessor 接口繼承了BeanPostProcessor 接口。

我們重點(diǎn)看看這兩個接口的關(guān)系调鬓,InstantiationAwareBeanPostProcessor 接口通過繼承 BeanPostProcessor 擁有了父接口的兩個方法艇炎,父類的兩個方法是在bean初始化前后做一些控制,而 InstantiationAwareBeanPostProcessor 自己增加的方法則是在bean的實(shí)例化做一些控制腾窝,順序:先執(zhí)行 InstantiationAwareBeanPostProcessor 自身的兩個實(shí)例化方法缀踪,再執(zhí)行父接口的初始化方法。

我們看到這些方法肯定會問虹脯,BeanPostProcessor 這個接口不是在初始化前和初始化后都能做一些操作嗎驴娃,為什么只叫后置處理器,應(yīng)該叫前后置處理器把唇敞?那么我們就去看看源碼,該方法被多處重寫咒彤,我們查看重寫該方法的所有的實(shí)現(xiàn)疆柔,可以看到所有處理該方法的邏輯都是直接返回bean,沒有任何邏輯操作蔼紧,可見該方法就是一個空方法婆硬。而 postProcessAfterInitialization 后置處理初始化方法各個實(shí)現(xiàn)類都有不同的操作。還有一個需要注意的的地方就是奸例,InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 這兩個方法名稱極其相似彬犯,注意區(qū)分,BeanPostProcessor 是初始化查吊,InstantiationAwareBeanPostProcessor 是實(shí)例化谐区,實(shí)例化先執(zhí)行,初始化后執(zhí)行逻卖。

為什么大張旗鼓的說后置處理器這個事情呢宋列?因?yàn)?AnnotationAwareAspectJAutoProxyCreator 就是后置處理器啊,他繼承了 AbstractAutoProxyCreator 抽象類评也,該類實(shí)現(xiàn)了后置處理器接口的方法炼杖。

既然是個后置處理器,那么就需要注冊盗迟,我們就看看注冊后置處理器的過程坤邪。

1.2 后置處理器注冊過程

我們會議一下Spring 容器初始化的方法 refresh ,當(dāng)時我們說罚缕,該方法是整個spring 的核心代碼艇纺,所有的邏輯都是從這里作為入口,那么該方法中有注冊后置處理器的邏輯嗎?

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 為刷新準(zhǔn)備應(yīng)用上下文
            prepareRefresh();
            // 告訴子類刷新內(nèi)部bean工廠黔衡,即在子類中啟動refreshBeanFactory()的地方----創(chuàng)建bean工廠蚓聘,根據(jù)配置文件生成bean定義
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 在這個上下文中使用bean工廠
            prepareBeanFactory(beanFactory);
            try {
                // 設(shè)置BeanFactory的后置處理器// 默認(rèn)什么都不做
                postProcessBeanFactory(beanFactory);
                // 調(diào)用BeanFactory的后處理器,這些后處理器是在Bean定義中向容器注冊的
                invokeBeanFactoryPostProcessors(beanFactory);
                // 注冊Bean的后處理器盟劫,在Bean創(chuàng)建過程中調(diào)用
                registerBeanPostProcessors(beanFactory);
                //對上下文的消息源進(jìn)行初始化
                initMessageSource();
                // 初始化上下文中的事件機(jī)制
                initApplicationEventMulticaster();
                // 初始化其他的特殊Bean
                onRefresh();
                // 檢查監(jiān)聽Bean并且將這些Bean向容器注冊
                registerListeners();
                // 實(shí)例化所有的(non-lazy-init)單件
                finishBeanFactoryInitialization(beanFactory);
                //  發(fā)布容器事件夜牡,結(jié)束refresh過程
                finishRefresh();
            } catch (BeansException ex) {
                // 為防止bean資源占用,在異常處理中捞高,銷毀已經(jīng)在前面過程中生成的單件bean
                destroyBeans();
                // 重置“active”標(biāo)志
                cancelRefresh(ex);
                // Propagate exception to caller.
                throw ex;
            } finally {
                resetCommonCaches();
            }
        }
    }

我們看一下代碼氯材,可以看到,其中有一行代碼硝岗,registerBeanPostProcessors(beanFactory)氢哮,注冊后置處理器,攜帶BeanFactory參數(shù)型檀,該方法會調(diào)用 PostProcessorRegistrationDelegate 的一個靜態(tài)方法:

該靜態(tài)方法很長冗尤,主要邏輯就是,從BeanFactory 中找到所有實(shí)現(xiàn)了 BeanPostProcessor 接口的bean胀溺,然后添加進(jìn)集合裂七,最后調(diào)用自身的 registerBeanPostProcessors 靜態(tài)方法,循環(huán)調(diào)用 beanFactory 的addBeanPostProcessor 方法仓坞,將后置處理器添加進(jìn)bean工廠背零。

bean工廠的 addBeanPostProcessor 方法如下:

將后置處理器放在一個集合中,并保證只有一個不會重復(fù)无埃。然后判斷是否 InstantiationAwareBeanPostProcessor 類型的后置處理器徙瓶,如果有,就將狀態(tài)hasInstantiationAwareBeanPostProcessors 改為 true嫉称。

這個時候也就是完成了后置處理器的注冊過程侦镇。

2. 標(biāo)注 @Aspect 注解的 bean 的后置處理器的處理過程

我們在使用 @Aspec 同時也會使用類似 @Component 的注解,表示這是一個Bean织阅,只要是bean壳繁,就會調(diào)用getBean方法,只要調(diào)用 getBean 方法荔棉,都會調(diào)用 AbstractAutowireCapableBeanFactory 的 createBean 方法闹炉,該方法其中有一行函數(shù)調(diào)用:

注釋說,會返回一個代理润樱,我們看看該方法到底是什么邏輯:

我們注意看該方法的 if 判斷渣触,hasInstantiationAwareBeanPostProcessors, 就是判斷我們剛剛設(shè)置的狀態(tài)祥国,向下走,獲取到Bean的類型,先調(diào)用 applyBeanPostProcessorsBeforeInstantiation 方法舌稀,如果該方法有返回值啊犬,則調(diào)用 applyBeanPostProcessorsAfterInitialization 方法,注意壁查,上面調(diào)用的是 “實(shí)例化前方法”觉至,下面那個調(diào)用的是 “初始化后方法”,是不同的接口實(shí)現(xiàn)睡腿。通常在第一次調(diào)用的的時候语御,是不會有返回值的。我們看看該方法實(shí)現(xiàn):

該方法會獲取所有的后置處理器席怪,就是之前注冊在BeanFactory 的后置處理器应闯,并循環(huán)調(diào)用他們的 postProcessBeforeInstantiation 方法,但是還是要判斷是不是 InstantiationAwareBeanPostProcessor 類型挂捻,我們關(guān)注的當(dāng)然是我們在之前的說的 AbstractAutoProxyCreator 后置處理器碉纺,我們看看他的 postProcessBeforeInstantiation 方法:

該方法首先檢驗(yàn)參數(shù),如果緩存中存在的話就直接返回刻撒。如果不在骨田,則判斷該bean的類型是否是基礎(chǔ)類型,上面是基礎(chǔ)類型声怔? 我們看代碼:

該方法首先調(diào)用父類的的 isInfrastructureClass 方法态贤,然后調(diào)用 isAspcet 方法。我們看看這兩個方法的實(shí)現(xiàn):

isInfrastructureClass

判斷是否是這幾個基礎(chǔ)類醋火。

isAspect

判斷該類是否含有Aspect 注解并且該類屬性中含有 “ajc$” 開頭的成員變量匕争。

那么后面的那個判斷呢?shouldSkip 伐脖,字面意思:是否應(yīng)該跳過秉犹。 該方法默認(rèn)實(shí)現(xiàn)是false,但該類注釋說晚树,子類需要重寫該方法姻采。我們看看該方法是如何重寫的:

首先找到所有候選的通知器,然后循環(huán)通知器數(shù)組爵憎,如果該通知器是 AspectJPointcutAdvisor 類型慨亲,并且該通知器的通知的切面名稱和bean的名字相同,就返回 true宝鼓。重點(diǎn)在如果獲取通知器刑棵,在XML配置中,我們知道愚铡,通知器是我們顯式的實(shí)現(xiàn)了 PointcutAdvisor 接口的類蛉签,并在配置文件中配置胡陪。而這里我們沒有配置。Spring 是如何找到的呢碍舍?我們看看 findCandidateAdvisors 方法柠座,該方法委托了 advisorRetrievalHelper 調(diào)用 findAdvisorBeans 方法,我們看看該方法實(shí)現(xiàn)片橡。
注意:findCandidateAdvisors 方法被重寫了妈经,我們看子類實(shí)現(xiàn)。

子類方法比父類方法多了一行 buildAspectJAdvisors捧书。

public List<Advisor> findAdvisorBeans() {
        // Determine list of advisor bean names, if not cached already.
        String[] advisorNames = null;
        synchronized (this) {
            advisorNames = this.cachedAdvisorBeanNames;
            if (advisorNames == null) {
                // Do not initialize FactoryBeans here: We need to leave all regular beans
                // uninitialized to let the auto-proxy creator apply to them!
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Advisor.class, true, false);
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
        if (advisorNames.length == 0) {
            return new LinkedList<Advisor>();
        }

        List<Advisor> advisors = new LinkedList<Advisor>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipping currently created advisor '" + name + "'");
                    }
                }
                else {
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
                        Throwable rootCause = ex.getMostSpecificCause();
                        if (rootCause instanceof BeanCurrentlyInCreationException) {
                            BeanCreationException bce = (BeanCreationException) rootCause;
                            if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Skipping advisor '" + name +
                                            "' with dependency on currently created bean: " + ex.getMessage());
                                }
                                // Ignore: indicates a reference back to the bean we're trying to advise.
                                // We want to find advisors other than the currently created bean itself.
                                continue;
                            }
                        }
                        throw ex;
                    }
                }
            }
        }
        return advisors;
    }

該方法會通過 BeanFactoryUtils.beanNamesForTypeIncludingAncestors 方法獲取容器中所有實(shí)現(xiàn)了Advisor 接口的Bean吹泡,然而我們的demo程序里上面都沒有。如果是XML就會返回了经瓷。

那我們看第二行的 this.aspectJAdvisorsBuilder.buildAspectJAdvisors()方法爆哑,該方法根據(jù)注解創(chuàng)建通知器。我們看看該方法邏輯:

    public List<Advisor> buildAspectJAdvisors() {
        List<String> aspectNames = this.aspectBeanNames;

        if (aspectNames == null) {
            synchronized (this) {
                aspectNames = this.aspectBeanNames;
                if (aspectNames == null) {
                    List<Advisor> advisors = new LinkedList<Advisor>();
                    aspectNames = new LinkedList<String>();
                    String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Object.class, true, false);
                    for (String beanName : beanNames) {
                        if (!isEligibleBean(beanName)) {
                            continue;
                        }
                        // We must be careful not to instantiate beans eagerly as in this case they
                        // would be cached by the Spring container but would not have been weaved.
                        Class<?> beanType = this.beanFactory.getType(beanName);
                        if (beanType == null) {
                            continue;
                        }
                        if (this.advisorFactory.isAspect(beanType)) {
                            aspectNames.add(beanName);
                            AspectMetadata amd = new AspectMetadata(beanType, beanName);
                            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                                MetadataAwareAspectInstanceFactory factory =
                                        new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                                List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                                if (this.beanFactory.isSingleton(beanName)) {
                                    this.advisorsCache.put(beanName, classAdvisors);
                                }
                                else {
                                    this.aspectFactoryCache.put(beanName, factory);
                                }
                                advisors.addAll(classAdvisors);
                            }
                            else {
                                // Per target or per this.
                                if (this.beanFactory.isSingleton(beanName)) {
                                    throw new IllegalArgumentException("Bean with name '" + beanName +
                                            "' is a singleton, but aspect instantiation model is not singleton");
                                }
                                MetadataAwareAspectInstanceFactory factory =
                                        new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                                this.aspectFactoryCache.put(beanName, factory);
                                advisors.addAll(this.advisorFactory.getAdvisors(factory));
                            }
                        }
                    }
                    this.aspectBeanNames = aspectNames;
                    return advisors;
                }
            }
        }

        if (aspectNames.isEmpty()) {
            return Collections.emptyList();
        }
        List<Advisor> advisors = new LinkedList<Advisor>();
        for (String aspectName : aspectNames) {
            List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
            if (cachedAdvisors != null) {
                advisors.addAll(cachedAdvisors);
            }
            else {
                MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
                advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
        }
        return advisors;
    }

方法很長了嚎,我們看看主要邏輯:先調(diào)用靜態(tài)方法 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false) 泪漂,也就是獲取容器中所有的bean,然后循環(huán)處理每個bean歪泳。這個 buildAspectJAdvisors 有個注意的地方就是萝勤,每次執(zhí)行該方法,最后都會更新 aspectBeanNames 屬性呐伞,該屬性是通知器的名稱集合敌卓。在循環(huán)中,會先創(chuàng)建一個 AspectMetadata 切面元數(shù)據(jù)對象伶氢,然后創(chuàng)建 BeanFactoryAspectInstanceFactory 對象趟径,在使用 advisorFactory 的 getAdvisors 方法攜帶剛剛創(chuàng)建的 factory 對象獲取該Class 對應(yīng)的所有 通知器。

我們看看 getAdvisors 的標(biāo)準(zhǔn)實(shí)現(xiàn):

    @Override
    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        validate(aspectClass);

        // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
        // so that it will only instantiate once.
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

        List<Advisor> advisors = new LinkedList<Advisor>();
        for (Method method : getAdvisorMethods(aspectClass)) {
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        // If it's a per target aspect, emit the dummy instantiating aspect.
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        // Find introduction fields.
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

在 getAdvisors 方法中癣防,會先根據(jù)Bean 的Class蜗巧,比如 Aop.class,和 切面名稱蕾盯,也就是benaNanme幕屹,然后根據(jù)class,獲取所有不帶 @Pointcut.class 的 Method 對象集合级遭,也就是 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 這些注解望拖。拿到這些方法數(shù)組后,進(jìn)行循環(huán)處理挫鸽,調(diào)用自己的 getAdvisor 方法说敏,我們看看 getAdvisor 方法。

    @Override
    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
            int declarationOrderInAspect, String aspectName) {

        validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

        AspectJExpressionPointcut expressionPointcut = getPointcut(
                candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
        if (expressionPointcut == null) {
            return null;
        }

        return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
    }

在該方法中丢郊,則根據(jù)method 對象獲取Pointcut盔沫,getPointcut 會將該方法上面的注解解析成一個 AspectJExpressionPointcut 切面表達(dá)式切入點(diǎn)對象医咨,最后 getAdvisor 將所有參數(shù)和 剛剛創(chuàng)建的對象 包裝成 InstantiationModelAwarePointcutAdvisorImpl 對象也就是 Advisor 的子類 返回。

回到 getAdvisors 方法中架诞, 該方法會將 getAdvisor 方法的返回值添加到 Advisors 數(shù)組中腋逆,然后如果數(shù)組不為空的話,創(chuàng)建一個合成的侈贷,沒有作用的通知器。 然后等脂,循環(huán)切面類的所有屬性俏蛮,解析含有 DeclareParents 注解的成員變量。

至此上遥,得到了該類所有的通知器搏屑。然后放入緩存。最后 粉楚,將 aspectBeanNames 屬性重新賦值辣恋。并返回 advisors。

回到 findCandidateAdvisors 方法模软,也就是尋找候選的通知器伟骨。然后將創(chuàng)建的通知器返回。

回到 AbstractAutoProxyCreator 的 postProcessBeforeInstantiation 的方法燃异, 該方法的 shouldSkip 判斷返回false携狭, 沒能進(jìn)入if塊返回,繼續(xù)向下走回俐。

首先尋找 Bean 的目標(biāo)類逛腿,我們并沒有設(shè)置目標(biāo)類,然后仅颇,就返回null单默。至此,resolveBeforeInstantiation 方法返回了null忘瓦。并沒有像注釋說的返回一個代理搁廓,除非設(shè)置了 customTargetSourceCreators。雖然這里也能創(chuàng)建代理政冻。而該方法也就結(jié)束了枚抵。該方法主要執(zhí)行了后置處理器的 “實(shí)例化之前” 方法。

3. 創(chuàng)建代理的過程

在執(zhí)行完 resolveBeforeInstantiation 返回 null 之后明场,就執(zhí)行 doCreateBean 方法汽摹,我們很熟悉這個方法,該方法在調(diào)用 populateBean 方法后完成了依賴注入苦锨,然后進(jìn)行初始化逼泣, 調(diào)用 initializeBean 方法趴泌。

initializeBean 方法

initializeBean 方法中調(diào)用了3個重要的邏輯,applyBeanPostProcessorsBeforeInitialization 方法 拉庶, 和 invokeInitMethods 方法(如果實(shí)現(xiàn)了InitializingBean 接口嗜憔,則會調(diào)用 afterPropertiesSet 方法),還有重要的 applyBeanPostProcessorsAfterInitialization 方法氏仗。

首先調(diào)用了前置處理器的 postProcessBeforeInitialization 方法吉捶,簡單的返回了bean。
然后判斷是否實(shí)現(xiàn) InitializingBean 接口從而決定是否調(diào)用 afterPropertiesSet 方法皆尔。

最后到了最重要的 applyBeanPostProcessorsAfterInitialization 方法呐舔, 該方法會循環(huán)調(diào)用每個bean的后置處理器的 postProcessAfterInitialization 方法。

現(xiàn)在慷蠕,來到了我們熟悉的 postProcessAfterInitialization 方法珊拼,該方法重寫了 BeanPostProcessor 接口的方法,然后進(jìn)入到 wrapIfNecessary 方法流炕。

首先判斷是否 targetSourcedBeans 目標(biāo)bean 集合中是否存在澎现,有則直接返回。
再判斷對應(yīng)的bean是否含有通知器每辟。
再判斷是不是基礎(chǔ)類剑辫,我們之前看過了,如果有渠欺,則返回true揭斧。也就會直接返回bean【撸或則調(diào)用 shouldSkip 方法判斷讹开,如果其中某個通知器 實(shí)現(xiàn)了AspectJPointcutAdvisor 接口,則返回 true捐名。我們的Bean 兩者都不是旦万,所以都是false,向下走镶蹋。

這里就是我們曾經(jīng)分析過的代碼成艘,今天再分析一下,主要還是創(chuàng)建代理贺归。首先獲取所有的通知器淆两。如果在后置處理器注冊的時候該Bean沒有設(shè)置通知器,則不會創(chuàng)建代理拂酣。然后還有就是我們之前注冊的后置處理器了秋冰。如過后置處理不是null,則再緩存中存儲bean的名字和是否有通知器的boolean值婶熬,再創(chuàng)建完代理后剑勾,將代理類和bean的名字存入代理類型緩存埃撵。以便以后可以隨時實(shí)例化代理。

createProxy 是我們的老朋友虽另,首先判斷 BeanFactory 是否實(shí)現(xiàn)了 ConfigurableListableBeanFactory 接口暂刘,默認(rèn)是實(shí)現(xiàn)了的。調(diào)用 AutoProxyUtils.exposeTargetClass 方法捂刺,

然后創(chuàng)建 ProxyFactory 實(shí)例谣拣,這個我們分析過了,然后將 AnnotationAwareAspectJAutoProxyCreator 數(shù)據(jù)復(fù)制到 ProxyFactory 中族展。

buildAdvisors 創(chuàng)建通知器數(shù)組芝发,根據(jù)bean的名稱,和剛剛注冊的通知器苛谷。我們看看該方法,該方法首先調(diào)用 resolveInterceptorNames 方法格郁,生成通知器數(shù)組腹殿,但我們沒有設(shè)置該屬性(XML 配置曾經(jīng)設(shè)置過)。 然后將我們給定的通知器添加到一個數(shù)組例书,然后判斷锣尉,如果配置了通知器,則將配置的通知器放在數(shù)組前面决采, 否則就放在后面自沧。

最后,循環(huán)該通知器數(shù)組树瞭,調(diào)用 advisorAdapterRegistry 的warp 方法拇厢,包裝通知器。最后返回晒喷,如何包裝呢孝偎?

我們看看warp 方法,判斷如果是Advisor 類型凉敲,則直接返回衣盾,如果不是Advice類型,拋出異常爷抓,如果是势决,則判斷是否是 MethodInterceptor 類型,如果是蓝撇,則包裝成 DefaultPointcutAdvisor 類型返回果复。如果都不滿足,則循環(huán)該類的adappters 適配器數(shù)組渤昌, 判斷如果支持 Advice 据悔,則也創(chuàng)建一個默認(rèn)的 DefaultPointcutAdvisor 返回传透。該適配器數(shù)組,默認(rèn)就有3個適配器极颓,一個before朱盐,一個after,一個throws菠隆。而我們的通知器都是 Advisor兵琳,所以直接返回了。

到這里 buildAdvisors 方法也就結(jié)束了骇径,返回了一個Advisors 數(shù)組躯肌。

回到 createProxy 方法, 得到了注冊的通知器破衔,于是 proxyFactory 可以設(shè)置 通知器屬性了清女,也設(shè)置了目標(biāo)類屬性,這時候就可以拿著工廠去創(chuàng)建代理了晰筛。

如何創(chuàng)建代理工廠我們就不看了嫡丙,因?yàn)槲覀冎耙呀?jīng)分析過了,我們的demo肯定創(chuàng)建的是 Cglib 代理读第,因此我們進(jìn)入到Cglib 的getProxy 方法中查看曙博。

    @Override
    public Object getProxy(ClassLoader classLoader) {
        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            Class<?> proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class<?> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }

            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass, classLoader);

            // Configure CGLIB Enhancer...
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
            enhancer.setSuperclass(proxySuperClass);
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

            Callback[] callbacks = getCallbacks(rootClass);
            Class<?>[] types = new Class<?>[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            // fixedInterceptorMap only populated at this point, after getCallbacks call above
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);

            // Generate the proxy class and create a proxy instance.
            return createProxyClassAndInstance(enhancer, callbacks);
        }
    }

首先判斷是否是Cglib類型,怎么判斷呢怜瞒?就判斷類名中是否含有兩個$$ 符號父泳,如果是的,則從當(dāng)前類中獲取所有接口吴汪,并添加到 advised 屬性中惠窄,很明顯,我們不是漾橙。

然后校驗(yàn)Class睬捶,寫日志。

創(chuàng)建一個增強(qiáng)器近刘。是 org.springframework.cglib.proxy 包下的擒贸。給增強(qiáng)器設(shè)置類加載器,設(shè)置父類觉渴,設(shè)置接口介劫,設(shè)置命名規(guī)則,默認(rèn)是 BySpringCGLIB案淋,設(shè)置策略(不知道是什么意思)座韵,,設(shè)置回調(diào)類型(七個回調(diào),都是CglibAopProxy 的內(nèi)部類)誉碴,設(shè)置了回調(diào)的過濾器宦棺,最后創(chuàng)建代理,再下面我們就不深究了黔帕。

我們從整個流程可以看到代咸,AOP的編織是通過定義@Aspect 和 @Around 等注解創(chuàng)建通知器,和我們在XML中編程幾乎一樣成黄,只是Sprng 向我們屏蔽了底層呐芥。最后,Spring 拿著目標(biāo)類和通過器去創(chuàng)建代理奋岁。

4. 目標(biāo)對象方法調(diào)用的過程

那么創(chuàng)建代理后做什么呢思瘟?

這時候就需要調(diào)用被 @PostConstruct 注解 的方法了,該注解的作用就是在Spring 實(shí)例化該bena后執(zhí)行該方法闻伶。

此時返回的已經(jīng)是代理類了滨攻,代理類執(zhí)行testController 方法,代理類內(nèi)部回調(diào)了 CglibAopProxy 的內(nèi)部類 DynamicAdvisedInterceptor 的 intercept 方法蓝翰,

        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Class<?> targetClass = null;
            Object target = null;
            try {
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                target = getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = methodProxy.invoke(target, argsToUse);
                }
                else {
                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                }
                retVal = processReturnType(proxy, target, method, retVal);
                return retVal;
            }
            finally {
                if (target != null) {
                    releaseTarget(target);
                }
                if (setProxyContext) {
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }

首先獲取目標(biāo)類光绕, 然后傳入目標(biāo)方法和 目標(biāo)類獲取通知器鏈, 最終調(diào)用 DefaultAdvisorChainFactory 的 getInterceptorsAndDynamicInterceptionAdvice 方法霎箍, 該方法參數(shù)為 AdvisedSupport, 目標(biāo)方法澡为,目標(biāo)類漂坏。我們進(jìn)入該方法查看:

    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, Class<?> targetClass) {

        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        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));
            }
        }

        return interceptorList;
    }

首先調(diào)用 hasMatchingIntroductions 方法,返回false媒至,該方法會判斷通知器是否是 IntroductionAdvisor 類型顶别,如果是,則判斷類過濾器是否匹配當(dāng)前的目標(biāo)類型拒啰。我們這里不是 IntroductionAdvisor 類型驯绎,因此都返回false。然后獲取全局的三個通知器適配器谋旦,然后循環(huán)匹配剩失,最后將匹配成功的包裝到 InterceptorAndDynamicMethodMatcher 中后添加到攔截器數(shù)組中。

回到 intercept ,得到了攔截器數(shù)組册着,如果攔截器數(shù)組是空的并且方法修飾符是public 的拴孤,就直接調(diào)用該方法,如果不是甲捏,則創(chuàng)建一個Cglib 方法調(diào)用器演熟。并執(zhí)行proceed方法,

由于我們的方法不是public 的,則執(zhí)行 else 邏輯芒粹,我們看看該 proceed 方法兄纺。

    @Override
    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í)行這里
        }
    }

proceed 方法會先獲取第一個攔截器,如果攔截器不是 InterceptorAndDynamicMethodMatcher 類型的化漆,則執(zhí)行攔截器的invoke 方法估脆。

在invoke 方法中,先將 mehodInvocation 對象放置在ThreadLocal 中获三,然后又回調(diào)自己的proceed方法旁蔼,判斷當(dāng)前攔截器下標(biāo)是否到了最大。再次執(zhí)行的攔截器也就是通知器是 AspectJAroundAdvice 的invoke 方法疙教,也就是我們定義的@Around 注解的方法棺聊。

    @Override
    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);
    }

首先創(chuàng)建一個我們熟知的 ProceedingJoinPoint 對象,然后調(diào)用 invokeAdviceMethod 方法贞谓,是其抽象父類的AbstractAspectJAdvice 的方法限佩。該方法繼續(xù)調(diào)用invokeAdviceMethodWithGivenArgs 方法。

    protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
        Object[] actualArgs = args;
        if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
            actualArgs = null;
        }
        try {
            ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
            // TODO AopUtils.invokeJoinpointUsingReflection
            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();
        }
    }

終于裸弦,在這里調(diào)用我們的test方法了祟同。執(zhí)行反射方法this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs) 。

然后回調(diào)自己的proceed 方法理疙,回到了 ReflectiveMethodInvocation 的 proceed 方法晕城,繼續(xù)判斷是否還有攔截器,沒有則執(zhí)行 invokeJoinpoint 方法窖贤,也就是目標(biāo)類本身的方法砖顷。

如果方法是public 的,則直接執(zhí)行方法代理的invoke方法赃梧,如果不是滤蝠,執(zhí)行父類的 invokeJoinpoint 方法, 實(shí)際上是執(zhí)行的 AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments) 方法授嘀,該方法執(zhí)行反射方法 method.invoke 物咳,最終執(zhí)行目標(biāo)方法。

最終返回到攔截器的方法中蹄皱,也就是我們的Aop.tesst 方法览闰,繼續(xù)執(zhí)行下面的邏輯。

執(zhí)行完畢巷折,AbstractAspectJAdvice 通知器的invokeAdviceMethod 的方法結(jié)束焕济,開始逐層返回。返回到 intercept 方法盔几, 繼續(xù)向下執(zhí)行晴弃。下面就是一些處理返回值的邏輯,最后返回返回值。完成了代理的一次調(diào)用上鞠。

image.png

5. 總結(jié)

這篇文章可以說是非常的長际邻,從 AOP 的切面編織,到代理的生成芍阎,到方法的調(diào)用世曾,可以說,非常的復(fù)雜谴咸。 不過轮听,Spring 中最復(fù)雜的 AOP 我們已經(jīng)徹底搞清楚了他的原理,功夫不負(fù)苦心人岭佳。不論是老舊的 XML 配置方式血巍,還是新的 SpringBoot 方式,我們都能夠了解他的原理珊随,在以后的程序排錯中述寡,也更加游刃有余,實(shí)際上叶洞,我們得到的不止是這個鲫凶,我們知道了Spring 的眾多擴(kuò)展接口,這些接口是AOP 賴以生存的條件衩辟,正式通過他們螟炫,完成了AOP的增強(qiáng)。我們也知道了艺晴,無論是注解還是配置文件昼钻,都是依賴同一個底層原理,這也警醒了我們财饥,無論多么復(fù)雜的系統(tǒng)换吧,底層都是相似的折晦,通過層層封裝后變得更加好用的同時也變得難以理解钥星,但只要明白底層原理,一切都沒那么復(fù)雜满着,樓主認(rèn)為底層原理更是網(wǎng)絡(luò)谦炒,OS,計組风喇,編譯原理宁改,數(shù)據(jù)結(jié)構(gòu)這些。山高路遠(yuǎn)魂莫,我們將一直走下去还蹲。

good luck !!!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谜喊,更是在濱河造成了極大的恐慌潭兽,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斗遏,死亡現(xiàn)場離奇詭異山卦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诵次,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門账蓉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人逾一,你說我怎么就攤上這事铸本。” “怎么了嬉荆?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵归敬,是天一觀的道長。 經(jīng)常有香客問我鄙早,道長汪茧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任限番,我火速辦了婚禮舱污,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弥虐。我一直安慰自己扩灯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布霜瘪。 她就那樣靜靜地躺著珠插,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颖对。 梳的紋絲不亂的頭發(fā)上捻撑,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音缤底,去河邊找鬼顾患。 笑死,一個胖子當(dāng)著我的面吹牛个唧,可吹牛的內(nèi)容都是我干的江解。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼徙歼,長吁一口氣:“原來是場噩夢啊……” “哼犁河!你這毒婦竟也來了鳖枕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤桨螺,失蹤者是張志新(化名)和其女友劉穎耕魄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彭谁,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吸奴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缠局。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片则奥。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖狭园,靈堂內(nèi)的尸體忽然破棺而出读处,到底是詐尸還是另有隱情,我是刑警寧澤唱矛,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布罚舱,位于F島的核電站,受9級特大地震影響绎谦,放射性物質(zhì)發(fā)生泄漏管闷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一窃肠、第九天 我趴在偏房一處隱蔽的房頂上張望包个。 院中可真熱鬧,春花似錦冤留、人聲如沸碧囊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糯而。三九已至,卻和暖如春泊窘,著一層夾襖步出監(jiān)牢的瞬間熄驼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工州既, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谜洽,地道東北人萝映。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓吴叶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親序臂。 傳聞我的和親對象是個殘疾皇子蚌卤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

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