Spring AOP源碼解析-核心組件解析及篩選合適的通知器

一.簡介

首先,我們先看這篇文章:AOP詳解,從這篇文章中回顧了關於AOP的一些專業術語。從這篇文章開始,我將會對Spring AOP部分的源碼來進行解析,本文主要分析Spring AOP是如何爲目標bean篩選合適的通知器。

二.核心組件解析

2.1 概述

Pointcut:用於定義攔截目標集合,祖先接口爲org.springframework.aop.Pointcut

Advice:用於定義攔截行爲。祖先接口爲org.aopalliance.aop.Advice,該接口只是標識接口,應用中可直接實現BeforeAdvice ,ThrowsAdvice,MethodInterceptor ,AfterReturningAdvice ,IntroductionInterceptor 等子接口

Advisor:充當Advice和Pointcut的適配器,類似使用Aspect的@Aspect註解的類(前一章節所述)。一般有advice和pointcut屬性。祖先接口爲org.springframework.aop.Advisor,應用中可直接使用org.springframework.aop.support.DefaultPointcutAdvisor

2.2 Pointcut

Pointcut決定Advice通知應該作用於哪個連接點,也就是說通過Pointcut來定義需要增強的方法的集合,這些集合可以按照一定的規則來完成。在這種情況下,Pointcut通常意味着標識方法,例如,這些需要增強的地方可以由某個正則表達式進行表示,或根據某個方法名進行匹配。

源碼如下:

public interface Pointcut {

    /** 返回一個類型過濾器 */
    ClassFilter getClassFilter();

    /** 返回一個方法匹配器 */
    MethodMatcher getMethodMatcher();

    Pointcut TRUE = TruePointcut.INSTANCE;
}

Pointcut 接口中定義了兩個接口,分別用於返回類型過濾器和方法匹配器。下面我們再來看一下類型過濾器和方法匹配器接口的定義:

public interface ClassFilter {

    /**
     * 判斷切入點於給定的接口或目標類是否類型匹配
     */
    boolean matches(Class<?> clazz);

    ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

public interface MethodMatcher {

    /**
     * 檢查是否有此方法的靜態匹配
     */
    boolean matches(Method method, Class<?> targetClass);

    /**
     * 是否運行時
     */
    boolean isRuntime();

    /**
     * 檢查是否有此方法的運行時(動態)匹配
     */
    boolean matches(Method method, Class<?> targetClass, Object... args);

    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

Pointcut接口作爲SpringAop中對AOP的最頂層抽象,主要負責對系統的相應的Joinpoint進行捕捉,並且提供了一個TruePointcut實例,當Pointcut爲TruePointcut類型時,則會忽略所有的匹配條件,對系統中所有的對象進行Joinpoint所定義的規則進行匹配;

ClassFilter與MethodMatcher分別用於在不同的級別上限定Joinpoint的匹配範圍,滿足不同粒度的匹配,ClassFilter限定在類級別上,MethodMatcher限定在方法級別上;但是SpringAop主要支持在方法級別上的匹配,所以對類級別的匹配支持相對簡單一些;
當傳入的clazz與Pointcut規定的類型一致時,則返回true,否則返回false,返回爲true時,則表示對這個類進行植入操作,當類型對Joinpoint的匹配不產生影響的時候,可以讓Pointcut接口中的ClassFilter getClassFilter()方法直接返回TrueClassFilter.INSTANCE,則表示對系統中的所有對象進行Joinpoint匹配;

MethodMatcher接口通過重載定義了兩個matches()方法,兩個參數的matches()被稱爲靜態匹配,在匹配條件不是太嚴格時使用,可以滿足大部分場景的使用,稱之爲靜態的主要是區分爲三個參數的matches()方法需要在運行時動態的對參數的類型進行匹配;兩個方法的分界線就是boolean isRuntime()方法,進行匹配時先用兩個參數的matches()方法進行匹配,若匹配成功,則檢查boolean isRuntime()的返回值,若爲true,則調用三個參數的matches()方法進行匹配(若兩個參數的都匹配不中,三個參數的必定匹配不中),比如需要統計用戶登錄次數時,那麼登錄傳入的參數就是可以忽略的,則調用兩個參數的matches()方法就已經足夠了,但是若要在登陸時對用戶賬號執行特殊的操作(如賦予特殊的操作權限),就需要對參數進行一個類似於檢驗的操作,就需要調用三個參數的matches()進行匹配。(上面三段內容來自:SpringAop中的Pointcut、ClassFilter、MethodMatcher接口的定義

接下來簡單介紹一下Spring提供的切點類型:

  • 靜態方法切點:org.springframework.aop.support.StaticMethodMatcherPointcut,靜態方法切點的抽象基類,默認情況下匹配所有的類。最常用的兩個子類NameMatchMethodPointcut和 AbstractRegexpMethodPointcut , 前者提供簡單字符串匹配方法簽名,後者使用正則表達式匹配方法簽名。
  • 動態方法切點:org.springframework.aop.support.DynamicMethodMatcherPointcut,動態方法切點的抽象基類,默認情況下匹配所有的類
  • 註解切點:org.springframework.aop.support.annotation.AnnotationMatchingPointcut
  • 表達式切點:org.springframework.aop.support.ExpressionPointcut,提供了對AspectJ切點表達式語法的支持
  • 流程切點:org.springframework.aop.support.ControlFlowPointcut,該切點是一個比較特殊的節點,它根據程序執行的堆棧信息查看目標方法是否由某一個方法直接或間接發起調用,一次來判斷是否爲匹配的鏈接點
  • 複合切點:org.springframework.aop.support.ComposablePointcut,該類是爲實現創建多個切點而提供的操作類

2.3 Advisor

完成對目標方法的切面通知設計(Advice)和關注點設計(Pointcut)以後,需要一個對象將它們結合起來,完成這個的作用就是Advisor(通知器)。通過Advisor,可以定義應該使用哪個通知並在哪個關注點上使用它,也就是通過Advisor,把Advice和Pointcut結合起來,這個結合爲使用IOC容器配置AOP應用,或者說即開即用地使用AOP基礎設施,提供了便利。接口定義如下:

public interface Advisor {

    Advice getAdvice();
    boolean isPerInstance();
}

public interface PointcutAdvisor extends Advisor {

    Pointcut getPointcut();
}

Advisor 中有一個 getAdvice 方法,用於返回通知。PointcutAdvisor 在 Advisor 基礎上,新增了 getPointcut 方法,用於返回切點對象。因此 PointcutAdvisor 的實現類即可以返回切點,也可以返回通知,所以說 PointcutAdvisor 和切面的功能相似。

切面可以分爲3類:一般切面、切點切面、引介切面

  • 一般切面Advisor
    org.springframework.aop.Advisor代表一般切面,僅包含一個Advice ,因爲Advice包含了橫切代碼和連接點信息,所以Advice本身一個簡單的切面,只不過它代表的橫切的連接點是所有目標類的所有方法,因爲這個橫切面太寬泛,所以一般不會直接使用。

  • 切點切面PointcutAdvisor
    org.springframework.aop.PointcutAdvisor ,代表具有切點的切面,包括Advice和Pointcut兩個類,這樣就可以通過類、方法名以及方位等信息靈活的定義切面的連接點,提供更具實用性的切面。PointcutAdvisor主要有6個具體的實現類:

    1. DefaultPointcutAdvisor:最常用的切面類型,它可以通過任意Pointcut和Advice定義一個切面,唯一不支持的就是引介的切面類型,一般可以通過擴展該類實現自定義的切面
    2. NameMatchMethodPointcutAdvisor:通過該類可以定義按方法名定義切點的切面
    3. AspectJExpressionPointcutAdvisor:用於AspectJ切點表達式定義切點的切面
    4. StaticMethodMatcherPointcutAdvisor:靜態方法匹配器切點定義的切面,默認情況下匹配所有的的目標類
    5. AspectJPointcutAdvisor:用於AspectJ語法定義切點的切面
  • 引介切面IntroductionAdvisor
    org.springframework.aop.IntroductionAdvisor代表引介切面, 引介切面是對應引介增強的特殊的切面,它應用於類層上面,所以引介切點使用ClassFilter進行定義。

三.源碼分析

3.1 AOP入口分析

首先,我們要思考一個問題,Spring是如何將AOP和IOC模塊整合到一起的?

在之前的文章:Bean的生命週期,我們瞭解到bean的整個生命週期過程,在這個過程中,Spring就將AOP和IOC模塊整合到一起。即通過擴展BeanPostProcessor接口,Spring AOP抽象代理創建器實現了BeanPostProcessor接口,並在bean初始化後置過程中向bean織入通知,即Spring加載這個bean時會在實例化前調用其postProcessAfterInitialization方法,這個方法的入口可以查看這篇文章:餘下的初始化工作的處理器應用。

Spring AOP 生成代理類的邏輯是在 AbstractAutoProxyCreator 相關子類中實現的,比如 DefaultAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator 等。上面說了 BeanPostProcessor 爲拓展留下了可能,這裏 AbstractAutoProxyCreator 就將可能變爲了現實。AbstractAutoProxyCreator 實現了 BeanPostProcessor 接口,這樣 AbstractAutoProxyCreator 可以在 bean 初始化時做一些事情。光繼承這個接口還不夠,繼承這個接口只能獲取 bean,要想讓 AOP 生效,還需要拿到切面對象(包含 Pointcut 和 Advice)纔行。所以 AbstractAutoProxyCreator 同時繼承了 BeanFactoryAware 接口,通過實現該接口,AbstractAutoProxyCreator 子類就可拿到 BeanFactory,有了 BeanFactory,就可以獲取 BeanFactory 中所有的切面對象了。有了目標對象 bean,所有的切面類,此時就可以爲 bean 生成代理對象了。

下面,就來分析相關源碼,如下:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    
    @Override
    /** bean 初始化後置處理方法 */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // 如果需要,爲 bean 生成代理對象
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return 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;
        }

        /*
         * 如果是基礎設施類(Pointcut、Advice、Advisor 等接口的實現類),或是應該跳過的類,
         * 則不應該生成代理,此時直接返回 bean
         */ 
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            // 將 <cacheKey, FALSE> 鍵值對放入緩存中,供上面的 if 分支使用
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 爲目標 bean 查找合適的通知器
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        /*
         * 若 specificInterceptors != null,即 specificInterceptors != DO_NOT_PROXY,
         * 則爲 bean 生成代理對象,否則直接返回 bean
         */ 
        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());
            /*
             * 返回代理對象,此時 IOC 容器輸入 bean,得到 proxy。此時,
             * beanName 對應的 bean 是代理對象,而非原始的 bean
             */ 
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        // specificInterceptors = null,直接返回 bean
        return bean;
    }
}

總結一下這個入口方法的工作流程:

  1. 若bean爲AOP基礎設施類(Pointcut、Advice、Advisor 等接口的實現類),則直接返回
  2. 爲bean查找合適的通知器
  3. 若通知數組不爲空,則爲bean生成代理對象,並返回該對對象
  4. 若數組爲空,則返回原始bean

3.2 篩選合適的通知器

在向bean織入通知之前,我們首先要爲bean篩選出合適的通知器(通知器持有通知)。如何篩選呢?方法有很多,比如我們可以通過正則表達式來匹配方法名,當然更多的是使用AspectJ表達式來進行匹配,下面來看一下使用AspectJ表達式篩選通知器的過程,如下:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
    // 查找合適的通知器
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 查找所有的通知器
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    /*
     * 篩選可應用在 beanClass 上的 Advisor,通過 ClassFilter 和 MethodMatcher
     * 對目標類和方法進行匹配
     */
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 拓展操作
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

對於指定bean的通知器的獲取一定是包含兩個步驟的,獲取所有通知器以及尋找所有通知器中適用bean的通知器並應有,那麼findCandidateAdvisors和findAdvisorsThatCanApply便是做了這兩件事情。當然,如果無法找到對應的通知器,便返回DO_NOT_PROXY,其中DO_NOT_PROXY==null。

3.2.1 查找通知器

Spring提供了兩種配置AOP的方式,一種是通過XML進行配置,另一種是基於註解的。詳細可以查看:AOP實驗

由於我們分析的是使用基於註解進行AOP,所以對於findCandidateAdvisors的實現其實是由AnnotationAwareAspectJAutoProxyCreator類完成的,現在,對源碼進行解析:

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

    //...

    @Override
    protected List<Advisor> findCandidateAdvisors() {
        // 調用父類方法從容器中查找所有的通知器
        List<Advisor> advisors = super.findCandidateAdvisors();
        // 解析 @Aspect 註解,並構建通知器
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        return advisors;
    }

    //...
}

AnnotationAwareAspectJAutoProxyCreator覆寫了父類的方法findCandidateAdvisors,並增加了一步操作,即解析@Aspect註解,並構建成通知器。下面來分析一下父類findCandidateAdvisors方法的邏輯,然後再來分析buildAspectJAdvisors方法的邏輯。

3.2.1.1 findCandidateAdvisors

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

    private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;
    
    //...

    protected List<Advisor> findCandidateAdvisors() {
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

    //...
}

從上面源碼可以看出,AbstractAdvisorAutoProxyCreator 中的findCandidateAdvisors是個空殼方法,所有邏輯封裝在BeanFactoryAdvisorRetrievalHelper 的findAdvisorBeans方法中。BeanFactoryAdvisorRetrievalHelper 可以理解爲從bean容器中獲取Advisor的幫助類,findAdvisorBeans則理解爲查找Advisor類型的bean,這個方法的就是從bean容器中將Advisor類型的bean查找出來。下來分析一下這個方法的源碼,如下:

public List<Advisor> findAdvisorBeans() {
    String[] advisorNames = null;
    synchronized (this) {
        // cachedAdvisorBeanNames 是 advisor 名稱的緩存
        advisorNames = this.cachedAdvisorBeanNames;
        /*
         * 如果 cachedAdvisorBeanNames 爲空,這裏到容器中查找,
         * 並設置緩存,後續直接使用緩存即可
         */ 
        if (advisorNames == null) {
            // 從容器中查找 Advisor 類型 bean 的名稱
            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>();
    // 遍歷 advisorNames
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            // 忽略正在創建中的 advisor bean
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    /*
                     * 調用 getBean 方法從容器中獲取名稱爲 name 的 bean,
                     * 並將 bean 添加到 advisors 中
                     */ 
                    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());
                            }
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }

    return advisors;

這個方法完成了兩件事情:

  1. 從容器中查找所有類型爲Advisor的bean對應的名稱
  2. 遍歷advisorNames,並從容器中獲取對應的bean

3.2.1.2 buildAspectJAdvisors

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>();
                // 從容器中獲取所有 bean 的名稱
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // 遍歷 beanNames
                for (String beanName : beanNames) {
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    
                    // 根據 beanName 獲取 bean 的類型
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }

                    // 檢測 beanType 是否包含 Aspect 註解
                    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 {
                            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();
    }
    //從緩存中取出放去advisors
    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;
}

buildAspectJAdvisors這個方法的工作流程如下:

  1. 獲取容器中所有bean的名稱(beanName)
  2. 遍歷上一步獲取的bean名稱數組,並獲取當前beanName對應的bean類型(beanType)
  3. 根據beanType判斷當前bean是否是一個Aspect註解類,若不是則不做任何處理
  4. 調用this.advisorFactory.getAdvisors(factory)獲取通知器
  5. 放入緩存中,然後從緩存中取出,返回

advisorFactory.getAdvisors(factory),這個方法及它調用的方法完成的工作有,獲得不含有@Pointcut註解的方法,對這個方法調用getAdvisor獲得AspectJ表達式切點和Advisor實現類,這個實現類中有Advice類型。源碼解析如下:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // 獲取 aspectClass 和 aspectName
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);

    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new LinkedList<Advisor>();

    // getAdvisorMethods 用於返回不包含 @Pointcut 註解的方法
    for (Method method : getAdvisorMethods(aspectClass)) {
        // 爲每個方法分別調用 getAdvisor 方法
        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中爲每個不包含@Pointcut註解方法調用getAdvisor方法:

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

    // 創建 Advisor 實現類
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

getAdvisor方法中包含兩個步驟,一個是獲取AspectJ表達式切點,另一個創建Advisor實現類。在第二個步驟中,包含一個隱藏步驟-創建Advice。

下面按照順序依次分析這兩個步驟,先看獲取Aspect表達式切點(getPointcut)的過程,如下:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // 獲取方法上的 AspectJ 相關注解,包括 @Before,@After 等
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // 創建一個 AspectJExpressionPointcut 對象
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    // 設置切點表達式
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    ajexp.setBeanFactory(this.beanFactory);
    return ajexp;
}

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
    // classesToLookFor 中的元素是大家熟悉的
    Class<?>[] classesToLookFor = new Class<?>[] {
            Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
    for (Class<?> c : classesToLookFor) {
        // 查找註解
        AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
        if (foundAnnotation != null) {
            return foundAnnotation;
        }
    }
    return null;
}

要注意的是,在@After等中的表達式很有可能是@Pointcut抽取出來的表達式,也就是說ajexp設置的表達式只是一箇中間表達式,不是最終值。後續要對這個表達式進行轉換,這個轉換過程現在就不分析了。

下面再來看看Advisor實現類的創建過程,如下:

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
        Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    this.declaredPointcut = declaredPointcut;
    this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    this.methodName = aspectJAdviceMethod.getName();
    this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    this.aspectJAdviceMethod = aspectJAdviceMethod;
    this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    this.aspectInstanceFactory = aspectInstanceFactory;
    this.declarationOrder = declarationOrder;
    this.aspectName = aspectName;

    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Pointcut preInstantiationPointcut = Pointcuts.union(
                aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

        this.pointcut = new PerTargetInstantiationModelPointcut(
                this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
        this.lazy = true;
    }
    else {
        this.pointcut = this.declaredPointcut;
        this.lazy = false;

        // 按照註解解析 Advice
        this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
    }
}

上面是InstantiationModelAwarePointcutAdvisorImpl的構造方法,目前不關注這個方法中的一些初始化邏輯,現在來看構造方法中的最後一行代碼,instantiateAdvice(this.declaredPointcut),這個方法用於創建通知Advice。通知器Advisor是通知Advice的持有者,所以在Advisor實現類的構造方法創建通知也是合適,下來看看構建通知的過程是怎樣的,如下:

private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
    return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
            this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    validate(candidateAspectClass);

    // 獲取 Advice 註解
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    if (!isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: " +
                "Offending method '" + candidateAdviceMethod + "' in class [" +
                candidateAspectClass.getName() + "]");
    }

    if (logger.isDebugEnabled()) {
        logger.debug("Found AspectJ method: " + candidateAdviceMethod);
    }

    AbstractAspectJAdvice springAdvice;

    // 按照註解類型生成相應的 Advice 實現類
    switch (aspectJAnnotation.getAnnotationType()) {
        case AtBefore:    // @Before -> AspectJMethodBeforeAdvice
            springAdvice = new AspectJMethodBeforeAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        case AtAfter:    // @After -> AspectJAfterAdvice
            springAdvice = new AspectJAfterAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        case AtAfterReturning:    // @AfterReturning -> AspectJAfterAdvice
            springAdvice = new AspectJAfterReturningAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;

        case AtAfterThrowing:    // @AfterThrowing -> AspectJAfterThrowingAdvice
            springAdvice = new AspectJAfterThrowingAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;

        case AtAround:    // @Around -> AspectJAroundAdvice
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;

        /*
         * 什麼都不做,直接返回 null。從整個方法的調用棧來看,
         * 並不會出現註解類型爲 AtPointcut 的情況
         */ 
        case AtPointcut:    
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
            
        default:
            throw new UnsupportedOperationException(
                    "Unsupported advice type on method: " + candidateAdviceMethod);
    }

    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    /*
     * 獲取方法的參數列表名稱,比如方法 int sum(int numX, int numY), 
     * getParameterNames(sum) 得到 argNames = [numX, numY]
     */
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        // 設置參數名
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();
    return springAdvice;
}

這個方法的主要邏輯就是根據註解類型生成與之對應的通知對象。

下面來總結下獲取通知器getAdvisors整個過程的邏輯(進行了簡化),如下:

  1. 從目標方法中獲取不包含@Pointcut註解的方法列表
  2. 遍歷上一步獲取的方法列表,並調用getAdvisor獲取當前方法對應的Advisor
  3. 創建AspectJExpressPointcut對象,並從方法中的註解獲取表達式,最後設置到切點對象中
  4. 創建Advisor實現類對象InstantiationModelAwarePointcutAdvisorImpl
  5. 調用instantiateAdvice方法構建通知
  6. 調用getAdvice方法,並根據註解類型創建相應通知。

從上面方法中可以看到,Spring會根據不同的註解生成不同的增強器。下來我們一起來分析一下 AspectJMethodBeforeAdvice,也就是 @Before 註解對應的通知實現類。看看它的邏輯是什麼樣的。

3.2.1.3 AspectJMethodBeforeAdvice分析

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {

    public AspectJMethodBeforeAdvice(
            Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {

        super(aspectJBeforeAdviceMethod, pointcut, aif);
    }


    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        // 調用通知方法
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }

    @Override
    public boolean isBeforeAdvice() {
        return true;
    }

    @Override
    public boolean isAfterAdvice() {
        return false;
    }

}

protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
    // 調用通知方法,並向其傳遞參數
    return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        // 通過反射調用通知方法
        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();
    }
}

AspectJMethodBeforeAdvice 的源碼比較簡單,這裏我們僅關注 before 方法。這個方法調用了父類中的 invokeAdviceMethod,然後 invokeAdviceMethod 在調用 invokeAdviceMethodWithGivenArgs,最後在 invokeAdviceMethodWithGivenArgs 通過反射執行通知方法。

剩下的實現,大家可以自己去看看。

3.2.2 篩選合適的通知器

前面函數已經完成了所有通知器的解析,但是對所有通知器來講,並不一定都適用於當前bean,還要挑選出合適的通知器,也就是滿足我們配置的通配符的通知器,具體實現下findAdvisorsThatCanApply中。

protected List<Advisor> findAdvisorsThatCanApply(
        List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        // 調用重載方法
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
        // 篩選 IntroductionAdvisor 類型的通知器
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            continue;
        }

        // 篩選普通類型的通知器
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        /*
         * 從通知器中獲取類型過濾器 ClassFilter,並調用 matchers 方法進行匹配。
         * ClassFilter 接口的實現類 AspectJExpressionPointcut 爲例,該類的
         * 匹配工作由 AspectJ 表達式解析器負責,具體匹配細節這個就沒法分析了,我
         * AspectJ 表達式的工作流程不是很熟
         */
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        // 對於普通類型的通知器,這裏繼續調用重載方法進行篩選
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        return true;
    }
}

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // 使用 ClassFilter 匹配 class
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }

    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        return true;
    }

    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    /*
     * 查找當前類及其父類(以及父類的父類等等)所實現的接口,由於接口中的方法是 public,
     * 所以當前類可以繼承其父類,和父類的父類中所有的接口方法
     */ 
    Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for (Class<?> clazz : classes) {
        // 獲取當前類的方法列表,包括從父類中繼承的方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            // 使用 methodMatcher 匹配方法,匹配成功即可立即返回
            if ((introductionAwareMethodMatcher != null &&
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    return false;
}

以上是通知器篩選的過程,篩選的工作主要由 ClassFilter 和 MethodMatcher 完成。關於 ClassFilter 和 MethodMatcher 我在上面核心組件解析一節中已經說過了,這裏再說一遍吧。在 AOP 中,切點 Pointcut 是用來匹配連接點的,以 AspectJExpressionPointcut 類型的切點爲例。該類型切點實現了ClassFilter 和 MethodMatcher 接口,匹配的工作則是由 AspectJ 表達式解析器複雜。

3.2.3 拓展篩選出通知器列表

protected void extendAdvisors(List<Advisor> candidateAdvisors) {
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // 如果通知器列表是一個空列表,則啥都不做
    if (!advisors.isEmpty()) {
        boolean foundAspectJAdvice = false;
        /*
         * 下面的 for 循環用於檢測 advisors 列表中是否存在 
         * AspectJ 類型的 Advisor 或 Advice
         */
        for (Advisor advisor : advisors) {
            if (isAspectJAdvice(advisor)) {
                foundAspectJAdvice = true;
            }
        }

        /*
         * 向 advisors 列表的首部添加 DefaultPointcutAdvisor,
         * 至於爲什麼這樣做,我會在後續的文章中進行說明
         */
        if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
            advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
            return true;
        }
    }
    return false;
}

private static boolean isAspectJAdvice(Advisor advisor) {
    return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
            advisor.getAdvice() instanceof AbstractAspectJAdvice ||
            (advisor instanceof PointcutAdvisor &&
                     ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}

由源碼可以看出 extendAdvisors 是一個空殼方法,除了調用makeAdvisorChainAspectJCapableIfNecessary,該方法沒有其他更多的邏輯了。至於 makeAdvisorChainAspectJCapableIfNecessary 這個方法,該方法主要的目的是向通知器列表首部添加 DefaultPointcutAdvisor 類型的通知器,也就是 ExposeInvocationInterceptor.ADVISOR。至於添加此種類型通知器的意圖,我會在後面文章裏說明。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章