死磕Spring之AOP篇 - Spring AOP自動代理(二)篩選合適的通知器

月圓吖發表於2021-04-20

該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 Spring 原始碼分析 GitHub 地址 進行閱讀。

Spring 版本:5.1.14.RELEASE

在開始閱讀 Spring AOP 原始碼之前,需要對 Spring IoC 有一定的瞭解,可檢視我的 《死磕Spring之IoC篇 - 文章導讀》 這一系列文章

瞭解 AOP 相關術語,可先檢視 《Spring AOP 常見面試題) 》 這篇文章

該系列其他文章請檢視:《死磕 Spring 之 AOP 篇 - 文章導讀》

在上一篇《Spring AOP 自動代理(一)入口》文章中,分析了 Spring AOP 自動代理的入口是 AbstractAutoProxyCreator 物件,其中自動代理的過程主要分為下面兩步:

  1. 篩選出能夠應用於當前 Bean 的 Advisor
  2. 找到了合適 Advisor 則建立一個代理物件, JDK 動態代理或者 CGLIB 動態代理

本文就接著上篇文章來分析篩選合適的通知器的處理過程,包含 @AspectJAspectJ 註解的解析過程。這裡的“通知器”指的是 Advisor 物件。

回顧

// AbstractAutoProxyCreator.java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    /*
     * <1> 如果當前 Bean 已經建立過自定義 TargetSource 物件
     * 表示在上面的**例項化前置處理**中已經建立代理物件,那麼直接返回這個物件
     */
    if (StringUtils.hasLength(beanName)
            && this.targetSourcedBeans.contains(beanName))
    {
        return bean;
    }
    // <2> `advisedBeans` 儲存了這個 Bean 沒有必要建立代理物件,則直接返回
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    /*
     * <3> 不需要建立代理物件,則直接返回當前 Bean
     */
    if (isInfrastructureClass(bean.getClass()) // 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記介面)
            || shouldSkip(bean.getClass(), beanName)) // 應該跳過
    {
        // 將這個 Bean 不需要建立代理物件的結果儲存起來
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // <4> 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // <5> 如果有 Advisor,則進行下面的動態代理建立過程
    if (specificInterceptors != DO_NOT_PROXY) {
        // <5.1> 將這個 Bean 已建立代理物件的結果儲存至 `advisedBeans`
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // <5.2> 建立代理物件,JDK 動態代理或者 CGLIB 動態代理
        // 這裡傳入的是 SingletonTargetSource 物件,可獲取代理物件的目標物件(當前 Bean)
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // <5.3> 將代理物件的 Class 物件(目標類的子類)儲存
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // <5.4> 返回代理物件
        return proxy;
    }

    // <6> 否則,將這個 Bean 不需要建立代理物件的結果儲存起來
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    // <7> 返回這個 Bean 物件
    return bean;
}

在建立代理物件的過程中,上面方法的第 4 步呼叫 getAdvicesAndAdvisorsForBean(..) 方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)

// AbstractAutoProxyCreator.java
protected abstract Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,
			@Nullable TargetSource customTargetSource) throws BeansException;

抽象方法,交由子類實現

篩選出合適的 Advisor 的流程

  1. 解析出當前 IoC 容器所有 Advisor 物件

    1. 獲取當前 IoC 容器所有 Advisor 型別的 Bean

    2. 解析當前 IoC 容器中所有帶有 @AspectJ 註解的 Bean,將內部帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解的方法解析出對應的 PointcutAdvisor 物件,帶有 @DeclareParents 註解的欄位解析出 IntroductionAdvisor 物件

      @Around -> AspectJAroundAdvice,實現了 MethodInterceptor

      @Before -> AspectJMethodBeforeAdvice

      @After -> AspectJAfterAdvice,實現了 MethodInterceptor

      @AfterReturning -> AspectJAfterAdvice

      @AfterThrowing -> AspectJAfterThrowingAdvice,實現了 MethodInterceptor

  2. 篩選出能夠應用於這個 Bean 的 Advisor 們,主要通過 ClassFilter 類過濾器和 MethodMatcher 方法匹配器進行匹配

  3. 對篩選出來的 Advisor 進行擴充套件,例如子類會往首部新增一個 PointcutAdvisor 物件

  4. 對篩選出來的 Advisor 進行排序

    • 不同的 AspectJ 根據 @Order 排序

    • 同一個 AspectJ 中不同 Advisor 的排序,優先順序:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice

主要涉及到下面幾個類:

  • AbstractAdvisorAutoProxyCreator:支援從當前 Spring 上下文獲取所有 Advisor 物件
  • AnnotationAwareAspectJAutoProxyCreator:支援從帶有 @AspectJ 註解 Bean 中解析 Advisor 物件
  • BeanFactoryAspectJAdvisorsBuilder:Advisor 構建器,用於解析出當前 BeanFactory 中所有帶有 @AspectJ 註解的 Bean 中的 Advisor
  • ReflectiveAspectJAdvisorFactory:Advisor 工廠,用於解析 @AspectJ 註解的 Bean 中的 Advisor

AnnotationAwareAspectJAutoProxyCreator 繼承 AbstractAdvisorAutoProxyCreator,藉助 BeanFactoryAspectJAdvisorsBuilder 構建器,這個構建器又藉助 ReflectiveAspectJAdvisorFactory 工廠。

AbstractAdvisorAutoProxyCreator

org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator:支援從當前 Spring 上下文獲取所有 Advisor 物件,存在能應用與 Bean 的 Advisor 則建立代理物件

建構函式

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

    /** Advisor 檢索工具類 */
	@Nullable
	private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		super.setBeanFactory(beanFactory);
		if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
			throw new IllegalArgumentException(
					"AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
		}
        // 初始化工作
		initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
	}

	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 設定 Advisor 檢索工具類為 BeanFactoryAdvisorRetrievalHelperAdapter
		this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
	}
}

1. getAdvicesAndAdvisorsForBean 方法

getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource),篩選某個 Bean 合適的 Advisor,如下:

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
        Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    // 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    // 轉換成陣列並返回
    return advisors.toArray();
}

呼叫 findEligibleAdvisors(Class<?> beanClass, String beanName) 方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)

2. findEligibleAdvisors 方法

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    /*
     * <1> 解析出當前 IoC 容器所有的 Advisor 物件
     * 1. 本身是 Advisor 型別的 Bean,預設情況下都會
     * 2. 從帶有 @AspectJ 註解的 Bean 中解析出來的 Advisor,子類 AnnotationAwareAspectJAutoProxyCreator 會掃描並解析
     *    PointcutAdvisor:帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解的方法
     *       其中 Pointcut 為 AspectJExpressionPointcut,Advice 就是註解標註的方法
     *    IntroductionAdvisor:帶有 @DeclareParents 註解的欄位
     *
     * 未排序,獲取到的 Advisor 在同一個 AspectJ 中的順序是根據註解來的,@Around > @Before > @After > @AfterReturning > @AfterThrowing
     */
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    /*
     * <2> 篩選出能夠應用到 `beanClass` 上面的所有 Advisor 物件並返回
     * 也就是通過 ClassFilter 進行匹配,然後再通過 MethodMatcher 對所有方法進行匹配(有一個即可)
     * AspectJExpressionPointcut 就實現了 ClassFilter 和 MethodMatcher
     */
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    /*
     * <3> 抽象方法,交由子類擴充
     * 例如 AspectJAwareAdvisorAutoProxyCreator 的實現
     * 如果 `eligibleAdvisors` 中存在和 AspectJ 相關的 Advisor
     * 則會在 `eligibleAdvisors` 首部新增一個 DefaultPointcutAdvisor 物件,對應的 Advice 為 ExposeInvocationInterceptor 物件
     * 用於暴露 MethodInvocation 物件(Joinpoint 物件),儲存在 ThreadLocal 中,在其他地方則可以使用
     */
    extendAdvisors(eligibleAdvisors);
    // <4> 對 `eligibleAdvisors` 集合進行排序,根據 @Order 註解進行排序
    if (!eligibleAdvisors.isEmpty()) {
        // 不同的 AspectJ 根據 @Order 排序
        // 同一個 AspectJ 中不同 Advisor 的排序:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    // <5> 返回排序後的能夠應用到當前 Bean 的所有 Advisor
    return eligibleAdvisors;
}

該方法的處理過程如下:

  1. 呼叫 findCandidateAdvisors() 方法,解析出當前 IoC 容器所有的 Advisor 物件,得到 candidateAdvisors 集合,來源:
    • 本身是 Advisor 型別的 Bean,預設情況下都會
    • 從帶有 @AspectJ 註解的 Bean 中解析出來的 Advisor
  2. 呼叫 findAdvisorsThatCanApply(..) 方法,篩選出能夠應用到 beanClass 上面的所有 Advisor 物件並返回,得到 eligibleAdvisors 集合
    • 通過 ClassFilter 進行匹配,然後再通過 MethodMatcher 對所有方法進行匹配(有一個即可)
  3. 呼叫 extendAdvisors(List<Advisor> candidateAdvisors) 方法,對 eligibleAdvisors 進行處理
  4. 呼叫 sortAdvisors(List<Advisor> advisors) 方法,對 eligibleAdvisors 進行排序
  5. 返回排序後的能夠應用到當前 Bean 的所有 Advisor

接下來依次對上面的方法進行分析

2.1.1 findCandidateAdvisors 方法

findCandidateAdvisors(),該方法會去找符合條件的 Advisor 們,AbstractAdvisorAutoProxyCreator 的實現則是去找當前 IoC 容器中所有 Advisor 型別的 Bean,如下:

// AbstractAdvisorAutoProxyCreator.java
protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    // 藉助 BeanFactoryAdvisorRetrievalHelperAdapter 從 IoC 容器中查詢所有的 Advisor 物件
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

可以看到是藉助於 BeanFactoryAdvisorRetrievalHelperAdapter 去找 Advisor 型別的 Bean,如下:

// BeanFactoryAdvisorRetrievalHelperAdapter.java
public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    // <1> 先從快取中獲取所有 Advisor
    String[] advisorNames = this.cachedAdvisorBeanNames;
    // <2> 沒有快取
    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!
        // <2.1> 從當前 BeanFactory 容器中找到所有 Advisor 型別的 bean 的名稱
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory, Advisor.class, true, false);
        // <2.2> 放入快取中
        this.cachedAdvisorBeanNames = advisorNames;
    }
    // <3> 如果沒有 Advisor,則返回一個空集合
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }

    List<Advisor> advisors = new ArrayList<>();
    /*
     * <4> 遍歷所有的 Advisor 型別的 Bean 的名稱,獲取對應的 Bean
     */
    for (String name : advisorNames) {
        // <4.1> 判斷這個 Bean 是否有資格,預設為 true
        if (isEligibleBean(name)) {
            // <4.2> 正在初始化,則先跳過
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Skipping currently created advisor '" + name + "'");
                }
            }
            // <4.3> 否則,獲取對應的 Bean
            else {
                try {
                    // 依賴查詢到這個 Advisor 物件
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    // ...
                    throw ex;
                }
            }
        }
    }
    // <5> 返回 IoC 容器中所有的 Advisor
    return advisors;
}

該方法的處理過程如下:

  1. 先從快取中獲取所有 Advisor

  2. 沒有快取

    1. 從當前 BeanFactory 容器中找到所有 Advisor 型別的 Bean 的名稱
    2. 放入快取中
  3. 如果沒有 Advisor,則返回一個空集合

  4. 遍歷所有的 Advisor 型別的 Bean 的名稱,獲取對應的 Bean

    1. 判斷這個 Bean 是否有資格,預設為 true
    2. 正在初始化,則先跳過
    3. 否則,獲取對應的 Bean,依賴查詢到這個 Advisor 物件
  5. 返回 IoC 容器中所有的 Advisor

總結下來,就是從當前 Spring IoC 容器中找到所有 Advisor 型別的 Bean

2.2 findAdvisorsThatCanApply 方法

findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) 方法,從 candidateAdvisors 中找到能夠應用於 beanClass 的 Advisor,如下:

protected List<Advisor> findAdvisorsThatCanApply(
        List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        /*
         * 篩選出能夠應用到 `beanClass` 上面的所有 Advisor 物件並返回
         * 也就是通過 ClassFilter 進行匹配,然後再通過 MethodMatcher 對所有方法進行匹配(有一個即可)
         * AspectJExpressionPointcut 就實現了 ClassFilter 和 MethodMatcher
         */
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

可以看到是藉助於 AopUtils 工具類從 candidateAdvisors 中找到能夠應用於 beanClass 的 Advisor,如下:

// AopUtils.java
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    /*
     * <1> 遍歷所有的 Advisor 物件
     * 找到能夠應用當前 Bean 的 IntroductionAdvisor 物件,放入 `eligibleAdvisors` 集合中
     */
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor // 如果是 IntroductionAdvisor 型別
                && canApply(candidate, clazz)) // 且能夠應用到當前 Bean 中,通過其 ClassFilter 進行過濾
        {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    /*
     * <2> 遍歷所有的 Advisor 物件
     * 如果是 IntroductionAdvisor 型別,則會跳過,因為上面已經判斷過
     * 找到能夠應用當前 Bean 的 Advisor 物件,放入 `eligibleAdvisors` 集合中
     */
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        // 判斷是否能夠應用到這個 Bean 上面
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    // <3> 返回能夠應用到當前 Bean 的所有 Advisor 物件
    return eligibleAdvisors;
}

該方法的處理過程如下:

  1. 遍歷所有的 Advisor 物件,找到能夠應用當前 Bean 的 IntroductionAdvisor 物件,放入 eligibleAdvisors 集合中,需要滿足下面兩個條件

    • IntroductionAdvisor 型別
    • 能夠應用到當前 Bean 中,通過其 ClassFilter 進行過濾
  2. 遍歷所有的 Advisor 物件,找到能夠應用當前 Bean 的 Advisor 物件,放入 eligibleAdvisors 集合中;如果是 IntroductionAdvisor 型別,則會跳過,因為上面已經判斷過

  3. 返回能夠應用到當前 Bean 的所有 Advisor 物件

AopUtils#canApply 方法

如何判斷這個 Advisor 能夠應用於某個 Bean 都是呼叫 canApply(..) 方法如下:

// AopUtils.java
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        /*
         * 從 IntroductionAdvisor 中獲取 ClassFilter 類過濾器,判斷這個目標類是否符合條件
         */
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        /*
         * 根據 Pointcut 中的 ClassFilter 和 MethodFilter 進行過濾
         * 例如 Aspect 的實現類 AspectJExpressionPointcut
         */
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        // It doesn't have a pointcut so we assume it applies.
        // 否則,沒有 Pointcut,也就是沒有篩選條件,則都符合條件
        return true;
    }
}

如果 IntroductionAdvisor 型別的 Advisor 則通過 ClassFilter 類過濾器進行判斷即可;如果是 PointcutAdvisor 型別的 Advisor 則需要呼叫 canApply(..) 的過載方法進行判斷;否則,沒有 Pointcut,也就是沒有篩選條件,則都符合條件

AopUtils#canApply 過載方法

如何判斷 PointcutAdvisor 型別的 Advisor 能夠應用於某個 Bean 的過程如下:

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

    // <2> 獲取 MethodMatcher 方法匹配器
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    // <3> 如果方法匹配器為 TrueMethodMatcher,則預設都通過
    if (methodMatcher == MethodMatcher.TRUE) {
        // No need to iterate the methods if we're matching any method anyway...
        return true;
    }

    // <4> 如果方法匹配器為 IntroductionAwareMethodMatcher,則進行轉換
    // AspectJExpressionPointcut 就是 IntroductionAwareMethodMatcher 的實現類
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    /*
     * <5> 獲取目標類、以及實現的所有介面,並新增至 `classes` 集合中
     */
    Set<Class<?>> classes = new LinkedHashSet<>();
    // <5.1> 如果不是 java.lang.reflect.Proxy 的子類
    if (!Proxy.isProxyClass(targetClass)) {
        // 獲取目標類的 Class 物件(如果目標類是 CGLIB 代理物件,則獲取其父類的 Class 物件,也就得到了目標類)
        classes.add(ClassUtils.getUserClass(targetClass));
    }
    // <5.2> 獲取目標類實現的所有介面,如果目標類本身是一個介面,那麼就取這個目標類
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    /*
     * <6> 遍歷上面的 `classes` 集合
     */
    for (Class<?> clazz : classes) {
        // <6.1> 獲取這個 Class 物件的所有方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        // <6.2> 遍歷上一步獲取到的所有方法
        for (Method method : methods) {
            // <6.3> 使用方法匹配器對該方法進行匹配,如果匹配成功則直接返回 `true`
            // AspectJExpressionPointcut 底層就是通過 AspectJ 進行處理的
            if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    // <7> 一個方法都沒匹配則返回 `false`,表示這個 Advisor 不能應用到這個 Bean 上面
    return false;
}

該方法的處理過程如下:

  1. 使用 Pointcut 的 ClassFilter 匹配 targetClass,不通過則直接返回 false
  2. 獲取 Pointcut 的 MethodMatcher 方法匹配器,儲存至 methodMatcher
  3. 如果 methodMatcher 為 TrueMethodMatcher,則預設都通過,返回 true
  4. 如果 methodMatcher 為 IntroductionAwareMethodMatcher,則進行轉換,儲存至 introductionAwareMethodMatcher
    • AspectJExpressionPointcut 就是 IntroductionAwareMethodMatcher 的實現類
  5. 獲取目標類、以及實現的所有介面,並新增至 classes 集合中
    1. 如果不是 java.lang.reflect.Proxy 的子類,則獲取 targetClass 目標類的 Class 物件(如果目標類是 CGLIB 代理物件,則獲取其父類的 Class 物件,也就得到了目標類)
    2. 獲取 targetClass 目標類實現的所有介面,如果目標類本身是一個介面,那麼就取這個目標類
  6. 遍歷上面的 classes 集合
    1. 獲取這個 Class 物件的所有方法
    2. 遍歷上一步獲取到的所有方法
    3. 使用 methodMatcher 方法匹配器對該方法進行匹配,優先使用 introductionAwareMethodMatcher 方法匹配器,匹配成功則直接返回 true,說明有一個方法滿足條件即可
      • AspectJExpressionPointcut 底層就是通過 AspectJ 的表示式處理進行處理的
  7. 一個方法都沒匹配成功則返回 false,表示這個 Advisor 不能應用到這個 Bean 上面

總結下來,PointcutAdvisor 是根據 Pointcut 的 ClassFilter 對目標類進行過濾,如果通過的話,則通過 MethodMatcher 方法匹配器對目標類的方法進行匹配,有一個方法滿足條件就表示這個 PointcutAdvisor 可以應用於目標類

2.3 extendAdvisors 方法

extendAdvisors(List<Advisor> candidateAdvisors) 放,對篩選出來的 Advisor 進行擴充套件,抽象方法,我們來看到子類的實現:

// AspectJAwareAdvisorAutoProxyCreator.java
@Override
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

可以看到是藉助於 AspectJProxyUtils 工具類進行擴充套件,如下:

public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // Don't add advisors to an empty list; may indicate that proxying is just not required
    if (!advisors.isEmpty()) {
        boolean foundAspectJAdvice = false;
        // 遍歷所有 Advisor
        for (Advisor advisor : advisors) {
            // Be careful not to get the Advice without a guard, as this might eagerly
            // instantiate a non-singleton AspectJ aspect...
            // 判斷這個 Advisor 是否和 AspectJ 相關
            if (isAspectJAdvice(advisor)) {
                foundAspectJAdvice = true;
                break;
            }
        }
        // 如果 `advisors` 涉及到和 AspectJ 相關的 Advisor
        // 則向其首部新增一個 DefaultPointcutAdvisor 物件,對應的 Advice 為 ExposeInvocationInterceptor 物件
        // 用於暴露 MethodInvocation 物件(Joinpoint 物件),儲存在 ThreadLocal 中,在其他地方則可以使用
        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) );
}

處理過程很簡單,當存在和 AspectJ 相關的 Advisor(使用了 AspectJ 的註解這裡都是 true),則在首部新增一個 DefaultPointcutAdvisor 物件

新增的這個 Advisor 對應的 Advice 為 ExposeInvocationInterceptor 方法攔截器,用於暴露 MethodInvocation 物件(Joinpoint 物件),儲存在 ThreadLocal 中,在其他地方則可以使用

2.4 sortAdvisors 方法

sortAdvisors(List<Advisor> advisors) 方法,對篩選出來的 Advisor 進行排序,如下:

// AspectJAwareAdvisorAutoProxyCreator.java
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
    List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());
    for (Advisor element : advisors) {
        // 使用 AspectJPrecedenceComparator 比較器
        partiallyComparableAdvisors.add(
                new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR));
    }
    List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);
    if (sorted != null) {
        List<Advisor> result = new ArrayList<>(advisors.size());
        for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
            result.add(pcAdvisor.getAdvisor());
        }
        return result;
    }
    else {
        // AbstractAdvisorAutoProxyCreator
        // 使用 AnnotationAwareOrderComparator 比較器,通過 @Order 註解
        return super.sortAdvisors(advisors);
    }
}

AspectJPrecedenceComparator 是對 AnnotationAwareOrderComparator 的包裝,進行了擴充套件,排序不同型別的 Advice,詳細的過程這裡不展述了

我通過 Debug 打斷點得到的結論:

  • 不同的 AspectJ 根據 @Order 排序

  • 同一個 AspectJ 中不同 Advisor 的排序,優先順序如下:

    AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice

小結

到這裡我們可以一個結論,篩選合適的通知器的總的過程在 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors(..) 方法中進行,分為下面幾步:

  1. 找符合條件的 Advisor 們,在 AbstractAdvisorAutoProxyCreator 則是去找當前 IoC 容器中所有 Advisor 型別的 Bean
  2. 從上一步找到的 Advisor 篩選出能夠應用於當前 Bean 的 Advisor 們,主要是通過 Pointcut 的 ClassFilter 類過濾器和 MethodMatcher 方法匹配器進行判斷,有一個方法匹配這個 Advisor 即滿足條件
  3. 支援對找到的 Advisor 集合進行擴充套件,在子類中會往其首部新增一個方法攔截器為 ExposeInvocationInterceptor 的 PointcutAdvisor
  4. 對找到的合適的 Advisor 進行排序,排序結果如上所述

上面過程的第 1 步僅找到當前 IoC 容器中所有 Advisor 型別的 Bean,是不是沒有對 AspectJ 相關注解進行解析,這個過程在子類中實現,也就是接下來要講的內容

AnnotationAwareAspectJAutoProxyCreator

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator:支援從帶有 @AspectJ 註解 Bean 中解析 Advisor 物件

建構函式

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

	/**
	 * 用於指定哪些 Bean 能夠作為 Advisor
	 */
	@Nullable
	private List<Pattern> includePatterns;
	/**
	 * 解析 AspectJ 註解的 Advisor 工廠
	 */
	@Nullable
	private AspectJAdvisorFactory aspectJAdvisorFactory;
	/**
	 * 構建器模式,用於構建  AspectJ 註解的 Advisor
	 */
	@Nullable
	private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;

	@Override
	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 初始化 BeanFactoryAdvisorRetrievalHelperAdapter
		super.initBeanFactory(beanFactory);
        // 初始化 ReflectiveAspectJAdvisorFactory
		if (this.aspectJAdvisorFactory == null) {
			this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
		}
        // 初始化 BeanFactoryAspectJAdvisorsBuilderAdapter
		this.aspectJAdvisorsBuilder =
				new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
	}
}

2.1.2 findCandidateAdvisors 方法

findCandidateAdvisors(),該方法會去找符合條件的 Advisor 們,通過父類找到當前 IoC 容器中所有 Advisor 型別的 Bean,這裡又會解析出帶有 @AspectJ 註解的 Bean 中的 Advisor 們,如下:

protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    // <1> 呼叫父類方法,從 IoC 容器中查詢所有的 Advisor 型別的 Bean
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    // <2> 如果 AspectJ 解析器不為空,預設為 BeanFactoryAspectJAdvisorsBuilderAdapter
    if (this.aspectJAdvisorsBuilder != null) {
        // 解析所有帶有 @AspectJ 註解的 Bean
        // 其中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解的方法會被解析成一個 PointcutAdvisor 物件
        // 將解析出來的所有 Advisor 新增至 `advisors` 中
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    // <3> 返回 `advisors` 集合(當前 IoC 容器中解析出來的所有的 Advisor 物件)
    return advisors;
}

該方法的處理過程如下:

  1. 呼叫父類方法,從 IoC 容器中查詢所有的 Advisor 型別的 Bean,儲存至 advisors 中,可返回上面的 2.1.1 findCandidateAdvisors 方法 小節檢視
  2. 如果 AspectJ 解析器不為空,預設為 BeanFactoryAspectJAdvisorsBuilderAdapter,則通過它解析出 Advisor 來
    • 解析所有帶有 @AspectJ 註解的 Bean
    • 其中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解的方法會被解析成一個 PointcutAdvisor 物件
    • 將解析出來的所有 Advisor 新增至 advisors
  3. 返回 advisors 集合(當前 IoC 容器中解析出來的所有的 Advisor 物件)

BeanFactoryAspectJAdvisorsBuilderAdapter

關於 @AspectJ 註解的解析由 BeanFactoryAspectJAdvisorsBuilderAdapter 完成,如下:

// AnnotationAwareAspectJAutoProxyCreator.java
private class BeanFactoryAspectJAdvisorsBuilderAdapter extends BeanFactoryAspectJAdvisorsBuilder {

    public BeanFactoryAspectJAdvisorsBuilderAdapter(
            ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {

        super(beanFactory, advisorFactory);
    }

    @Override
    protected boolean isEligibleBean(String beanName) {
        return AnnotationAwareAspectJAutoProxyCreator.this.isEligibleAspectBean(beanName);
    }
}

protected boolean isEligibleAspectBean(String beanName) {
    if (this.includePatterns == null) {
        return true;
    } else {
        for (Pattern pattern : this.includePatterns) {
            if (pattern.matcher(beanName).matches()) {
                return true;
            }
        }
        return false;
    }
}

這裡只重寫了 isEligibleBean(String) 方法,用於判斷這個 Bean 是否有資格作為一個 Advisor。可以看到是通過 includePatternsbeanName 進行判斷,匹配通過才有資格。當然,includePatterns 一般為空,都有資格。

這個類繼承了 BeanFactoryAspectJAdvisorsBuilder 構建器,我們來看到這個構建器是如何解析的。

BeanFactoryAspectJAdvisorsBuilder

org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder,Advisor 構建器,用於解析出當前 BeanFactory 中所有帶有 @AspectJ 註解的 Bean 中的 Advisor

建構函式

public class BeanFactoryAspectJAdvisorsBuilder {
	/**
	 * 當前 IoC 容器,DefaultListableBeanFactory
	 */
	private final ListableBeanFactory beanFactory;
	/**
	 * Advisor 工廠,用於解析 @AspectJ 註解的 Bean 中的 Advisor
	 */
	private final AspectJAdvisorFactory advisorFactory;
	/**
	 * 用於快取帶有 @AspectJ 註解的 Bean 的名稱
	 */
	@Nullable
	private volatile List<String> aspectBeanNames;
	/**
	 * 快取 @AspectJ 註解的單例 Bean 中解析出來的 Advisor
	 * key:帶有 @AspectJ 註解的 beanName
	 * value:其內部解析出來的 Advisor 集合
	 */
	private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>();
	/**
	 * 快取 @AspectJ 註解的非單例 Bean 的後設資料例項構建工廠
	 * key:帶有 @AspectJ 註解的 beanName(非單例)
	 * value:對應的後設資料工廠物件
	 */
	private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache = new ConcurrentHashMap<>();

	public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
		Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
		Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
		this.beanFactory = beanFactory;
		this.advisorFactory = advisorFactory;
	}
}

2.1.3 buildAspectJAdvisors 方法

buildAspectJAdvisors() 方法,解析出當前 BeanFactory 中所有帶有 @AspectJ 註解的 Bean 中的 Advisor,如下:

public List<Advisor> buildAspectJAdvisors() {
    // <1> 從快取中獲取所有帶有 @AspectJ 註解的 Bean,儲存至 `aspectNames` 集合中
    List<String> aspectNames = this.aspectBeanNames;

    // <2> 快取中沒有,則對當前物件加鎖再判斷快取中是否有資料
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            // <3> 還是沒有快取,則進行接下來的處理
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                // <3.1> 獲取當前 IoC 容器中所有的 Bean 的名稱集合
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // <3.2> 遍歷所有的 Bean 的名稱,進行處理
                for (String beanName : beanNames) {
                    // <3.3> 判斷這個 Bean 是否有資格,預設都為 true
                    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.
                    // <3.4> 獲取這個 Bean 的 Class 物件,如果為空則跳過
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    // <3.5> 如果這個 Bean 帶有 @Aspect 註解,且沒有以 `ajc$` 開頭的欄位,那麼進行接下來的解析過程
                    if (this.advisorFactory.isAspect(beanType)) {
                        // <3.5.1>  將這個 Bean 的名稱儲存至 `aspectNames` 集合中
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        // <3.5.2> 判斷 @AspectJ 註解的類別是否為 `singleton`,預設空的情況就是這個
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            MetadataAwareAspectInstanceFactory factory =
                                    new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            // <3.5.2.1> 解析這個 Bean 中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解的方法
                            // 會解析成對應的 InstantiationModelAwarePointcutAdvisorImpl 物件(PointcutAdvisor)
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                            // <3.5.2.2> 如果這個 Bean 是單例模式,則將解析出來的 Advisor 全部快取至 `advisorsCache` 中
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            // <3.5.2.3> 否則,將這個 Bean 對應的 MetadataAwareAspectInstanceFactory(AspectJ 後設資料例項構建工廠)快取至 `aspectFactoryCache` 中
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            // <3.5.2.4> 將解析出來的 Advisor 新增至 `advisors` 中
                            advisors.addAll(classAdvisors);
                        }
                        // <3.5.3> 否則,這個 AspectJ 不是單例模式,不能將解析出來的 Advisor 快取,其他的處理過程都和上面一樣
                        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);
                            // 將這個 Bean 對應的 MetadataAwareAspectInstanceFactory(AspectJ 後設資料例項構建工廠)快取至 `aspectFactoryCache` 中
                            this.aspectFactoryCache.put(beanName, factory);
                            // 解析出這個 Bean 中所有的 Advisor,並新增至 `advisors` 中
                            advisors.addAll(this.advisorFactory.getAdvisors(factory));
                        }
                    }
                }
                // <3.6> 對 `aspectNames` 進行快取
                this.aspectBeanNames = aspectNames;
                // <3.7> 返回所有 AspectJ 的所有的 Advisor 物件們
                return advisors;
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    /*
     * <4> 否則,遍歷快取中的 AspectJ 的 beanName
     */
    for (String aspectName : aspectNames) {
        // <4.1> 嘗試從 `advisorsCache` 快取中獲取這個 beanName 對應的所有 Advisor 們,並新增至 `advisors` 中
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        // <4.2> `advisorsCache` 快取中沒有,
        // 則根據 `aspectFactoryCache` 快取中對應的 MetadataAwareAspectInstanceFactory(AspectJ 後設資料例項構建工廠)解析出所有的 Advisor 們,並新增至 `advisors` 中
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    // <5> 返回所有 AspectJ 的所有的 Advisor 物件們
    return advisors;
}

該方法的處理過程稍微有點複雜,如下:

  1. 從快取中獲取所有帶有 @AspectJ 註解的 Bean,儲存至 aspectNames 集合中
  2. 快取中沒有,則對當前物件加鎖再判斷快取中是否有資料
  3. 還是沒有快取,則進行接下來的處理
    1. 獲取當前 IoC 容器中所有的 Bean 的名稱集合
    2. 遍歷所有的 Bean 的名稱,進行處理
    3. 判斷這個 Bean 是否有資格,預設都為 true
    4. 獲取這個 Bean 的 Class 物件,如果為空則跳過
    5. 如果這個 Bean 帶有 @Aspect 註解,且沒有以 ajc$ 開頭的欄位,那麼進行接下來的解析過程
      1. 將這個 Bean 的名稱儲存至 aspectNames 集合中
      2. 判斷 @AspectJ 註解的類別是否為 singleton,預設空的情況就是這個
        1. 通過 AspectJAdvisorFactory#getAdvisors(..) 方法解析出這個 Bean 中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解的方法,例如會解析成 InstantiationModelAwarePointcutAdvisorImpl 物件(PointcutAdvisor)
        2. 如果這個 Bean 是單例模式,則將解析出來的 Advisor 全部快取至 advisorsCache
        3. 否則,將這個 Bean 對應的 MetadataAwareAspectInstanceFactory(AspectJ 後設資料例項構建工廠)快取至 aspectFactoryCache
        4. 將解析出來的 Advisor 新增至 advisors
      3. 否則,這個 AspectJ 不是單例模式,不能將解析出來的 Advisor 快取,其他的處理過程都和上面一樣
    6. aspectNames 進行快取
    7. 返回所有 AspectJ 的所有的 Advisor 物件們
  4. 否則,遍歷快取中的 AspectJ 的 beanName,進行處理
    1. 嘗試從 advisorsCache 快取中獲取這個 beanName 對應的所有 Advisor 們,並新增至 advisors
    2. advisorsCache 快取中沒有,則根據 aspectFactoryCache 快取中對應的 MetadataAwareAspectInstanceFactory(AspectJ 後設資料例項構建工廠)解析出所有的 Advisor 們,並新增至 advisors
  5. 返回所有 AspectJ 的所有的 Advisor 物件們

做個小結,整個過程稍微複雜一點,會嘗試從快取中獲取 Advisor,快取中沒有資料則先獲取到所有帶有 @AspectJ 註解的 Bean,通過 ReflectiveAspectJAdvisorFactory 對這些 Bean 中帶有 AspectJ 相關注解的方法進行處理,生成對應的 PointcutAdvisor 物件。

接下來,我們來看看 ReflectiveAspectJAdvisorFactory 解析 @AspectJ 註解的 Bean 的過程

ReflectiveAspectJAdvisorFactory

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory,Advisor 工廠,用於解析 @AspectJ 註解的 Bean 中的 Advisor

建構函式

public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
	/**
	 * 方法比較器
	 */
	private static final Comparator<Method> METHOD_COMPARATOR;

	static {
		Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
				new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
				(Converter<Method, Annotation>) method -> {
					AspectJAnnotation<?> annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
					return (annotation != null ? annotation.getAnnotation() : null);
				});
		Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
		METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
	}
}

可以看到有一個 Comparator 方法比較器,順序是 @Around > @Before > @After > @AfterReturning > @AfterThrowing,注意獲取到應用於某個 Bean 的 Advisor 的順序不是這樣子,可以回到前面的 2.4 sortAdvisors 方法 小節看看

2.1.3.1 getAdvisors 方法

getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) 方法,解析出這個 Bean(帶有 @AspectJ 註解)中所有的 Advisor,方法入參是 Bean 的後設資料例項構建工廠,方法如下:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // <1> 獲取這個 Bean 的 Class 物件和 beanName
    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 ArrayList<>();

    /*
     * <2> 遍歷沒有標註 @Pointcut 註解的方法(順序:@Around > @Before > @After > @AfterReturning > @AfterThrowing)
     */
    for (Method method : getAdvisorMethods(aspectClass)) {
        /*
         * <2.1> 如果這個方法帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解
         * 則根據註解資訊建立一個 InstantiationModelAwarePointcutAdvisorImpl 物件
         * 這個物件就是 PointcutAdvisor 型別,包含了 Pointcut 和 Advice
         */
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
        // <2.2> 生成了 PointcutAdvisor 則新增至 `advisor` 集合中
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    // If it's a per target aspect, emit the dummy instantiating aspect.
    // <3> 如果這個 Aspect 需要延遲初始化,則往首部新增一個 PointcutAdvisor
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    // Find introduction fields.
    // <4> 根據帶有 @DeclareParents 註解的欄位生成 IntroductionAdvisor 物件,並新增至 `advisor` 集合中
    for (Field field : aspectClass.getDeclaredFields()) {
        Advisor advisor = getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    // <5> 返回這個 Aspect 中所有的 Advisor 物件
    return advisors;
}

該方法的處理過程如下:

  1. 獲取這個 Bean 的 Class 物件和 beanName

  2. 遍歷沒有標註 @Pointcut 註解的方法(順序:@Around > @Before > @After > @AfterReturning > @AfterThrowing

    private List<Method> getAdvisorMethods(Class<?> aspectClass) {
       final List<Method> methods = new ArrayList<>();
       ReflectionUtils.doWithMethods(aspectClass, method -> {
          // Exclude pointcuts
          // 排除 @Pointcut 註解標註的方法
          if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
             methods.add(method);
          }
       });
       // 進行排序
       methods.sort(METHOD_COMPARATOR);
       return methods;
    }
    
    1. 呼叫 getAdvisor(..) 方法,將這個方法解析成 InstantiationModelAwarePointcutAdvisorImpl 物件(PointcutAdvisor),這個方法必須帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解
    2. 如果上一步解析出了 Advisor 物件,則新增至 advisor 集合中
  3. 如果這個 Aspect 需要延遲初始化,則往首部新增一個 SyntheticInstantiationAdvisor(PointcutAdvisor),暫時忽略

  4. 呼叫 getDeclareParentsAdvisor(..) 方法,根據帶有 @DeclareParents 註解的欄位生成 IntroductionAdvisor 物件,並新增至 advisor 集合中

    private Advisor getDeclareParentsAdvisor(Field introductionField) {
        DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
        if (declareParents == null) {
            return null;
        }
        if (DeclareParents.class == declareParents.defaultImpl()) {
            throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
        }
        return new DeclareParentsAdvisor(
                introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
    }
    
  5. 返回這個 Aspect 中所有的 Advisor 物件,也就是返回 advisor 集合

可以看到 @AspectJ 註解的 Bean 中的 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解方法在 getAdvisor(..) 方法中進行

2.1.3.2 getAdvisor 方法

getAdvisor(..) 方法,解析 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解方法,生成 PointcutAdvisor 物件,如下:

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

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

    /*
     * <1> 嘗試根據該方法生成一個 AspectJExpressionPointcut 物件
     * 根據 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解資訊進行建立,沒有的話則返回 null
     */
    AspectJExpressionPointcut expressionPointcut = getPointcut(
            candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }

    /*
     * <2> 如果存在上面其中一個註解,則將建立的 AspectJExpressionPointcut 封裝成 InstantiationModelAwarePointcutAdvisorImpl 物件
     * 也就是封裝成了 PointcutAdvisor 物件,會初始化一個 Advice,也就是註解標註的方法
     * 那麼這個物件中就包含了 Pointcut 和 Advice,就可以判斷某個方法是否被攔截,攔截後應該如何處理
     */
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

該方法的處理過程如下:

  1. 呼叫 getPointcut(..) 方法,嘗試根據該方法生成一個 AspectJExpressionPointcut 物件,根據 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解資訊進行建立,沒有的話則返回 null
  2. 如果存在上面其中一個註解,則將建立的 AspectJExpressionPointcut 封裝成 InstantiationModelAwarePointcutAdvisorImpl 物件,也就是封裝成了 PointcutAdvisor 物件,會初始化一個 Advice,也就是註解標註的方法。那麼這個物件中就包含了 Pointcut 和 Advice,就可以判斷某個方法是否被攔截,攔截後應該如何處理

getPointcut 方法

getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) 方法,嘗試將方法解析成 AspectJExpressionPointcut 物件,如下:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // <1> 找到這個方法的 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解資訊
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // <2> 如果帶有上面其中一個註解,則建立一個 AspectJExpressionPointcut 物件
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    // <3> 設定 Pointcut 的表示式
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    if (this.beanFactory != null) {
        ajexp.setBeanFactory(this.beanFactory);
    }
    // <4> 返回 AspectJExpressionPointcut 物件
    return ajexp;
}

該方法的處理過程如下:

  1. 找到這個方法的 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解資訊
  2. 如果帶有上面其中一個註解,則建立一個 AspectJExpressionPointcut 物件
  3. 設定 Pointcut 的表示式
  4. 返回 AspectJExpressionPointcut 物件

getAdvice 方法

getAdvice(..) 方法,主要根據 AspectJExpressionPointcut 初始化一個 Advice 物件,如下:

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

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

    // 獲取 @Pointcut、@Around、@Before、@After、@AfterReturning、@AfterThrowing 註解
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // If we get here, we know we have an AspectJ method.
    // Check that it's an AspectJ-annotated class
    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;

    switch (aspectJAnnotation.getAnnotationType()) {
        case AtPointcut:
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        case AtAround: // @Around -> AspectJAroundAdvice
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        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;
        default:
            throw new UnsupportedOperationException(
                    "Unsupported advice type on method: " + candidateAdviceMethod);
    }

    // Now to configure the advice...
    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    /*
     * 獲取方法的引數名列表
     */
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        // 設定引數名
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();

    return springAdvice;
}

根據註解的型別建立對應的 Advice 型別,如下:

  • @Around:AspectJAroundAdvice,實現了 MethodInterceptor
  • @Before:AspectJMethodBeforeAdvice
  • @After:AspectJAfterAdvice,實現了 MethodInterceptor
  • @AfterReturning: AspectJAfterAdvice
  • @AfterThrowing:AspectJAfterThrowingAdvice,實現了 MethodInterceptor

InstantiationModelAwarePointcutAdvisorImpl

org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl,AspectJ 註解方法解析後的物件,實現了 PointcutAdvisor,包含 Pointcut 和 Advice

final class InstantiationModelAwarePointcutAdvisorImpl
		implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation, Serializable {

	private static final Advice EMPTY_ADVICE = new Advice() {};

	private final AspectJExpressionPointcut declaredPointcut;

	private final Class<?> declaringClass;

	private final String methodName;

	private final Class<?>[] parameterTypes;

	private transient Method aspectJAdviceMethod;

	private final AspectJAdvisorFactory aspectJAdvisorFactory;

	private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;

	private final int declarationOrder;

	private final String aspectName;

	private final Pointcut pointcut;

	private final boolean lazy;

	@Nullable
	private Advice instantiatedAdvice;

	@Nullable
	private Boolean isBeforeAdvice;

	@Nullable
	private Boolean isAfterAdvice;

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

		// AspectJExpressionPointcut 物件
		this.declaredPointcut = declaredPointcut;
		// Advice 所在的 Class 物件
		this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
		// Advice 對應的方法名稱
		this.methodName = aspectJAdviceMethod.getName();
		// Advice 對應的方法引數型別
		this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
		// Advice 對應的方法物件
		this.aspectJAdviceMethod = aspectJAdviceMethod;
		// Advisor 工廠,用於解析 @AspectJ 註解的 Bean 中的 Advisor
		this.aspectJAdvisorFactory = aspectJAdvisorFactory;
		// 後設資料例項構建工廠
		this.aspectInstanceFactory = aspectInstanceFactory;
		// 定義的順序
		this.declarationOrder = declarationOrder;
		// Advice 所在 Bean 的名稱
		this.aspectName = aspectName;

		// 如果需要延遲初始化,則不立即初始化 Advice 物件
		if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
			// Static part of the pointcut is a lazy type.
			Pointcut preInstantiationPointcut = Pointcuts.union(
					aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

			// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
			// If it's not a dynamic pointcut, it may be optimized out
			// by the Spring AOP infrastructure after the first evaluation.
			this.pointcut = new PerTargetInstantiationModelPointcut(
					this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
			this.lazy = true;
		}
		// 否則,初始化 Advice 物件
		else {
			// A singleton aspect.
			// AspectJExpressionPointcut 物件
			this.pointcut = this.declaredPointcut;
			this.lazy = false;
			// 根據 AspectJExpressionPointcut 初始化一個 Advice 物件
			// `@Around`:AspectJAroundAdvice,實現了 MethodInterceptor
			// `@Before`:AspectJMethodBeforeAdvice
			// `@After`:AspectJAfterAdvice,實現了 MethodInterceptor
			// `@AfterReturning`: AspectJAfterAdvice
			// `@AfterThrowing`:AspectJAfterThrowingAdvice,實現了 MethodInterceptor
			this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
		}
	}
    
    private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
		Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
				this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
		return (advice != null ? advice : EMPTY_ADVICE);
	}
}

總結

在上一篇《Spring AOP 自動代理(一)入口》文章講述了 Spring AOP 自動代理的入口,主要對 AbstractAutoProxyCreator 這個類進行了分析。本文接著上一篇文章分析了在 Spring AOP 自動代理的的過程中,如何從 Spring 上下文篩選出能夠應用於某個 Bean 的 Advisor 們,大致的流程如下:

  1. 解析出當前 IoC 容器所有 Advisor 物件

    1. 獲取當前 IoC 容器所有 Advisor 型別的 Bean
    2. 解析當前 IoC 容器中所有帶有 @AspectJ 註解的 Bean,將內部帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 註解的方法解析出對應的 PointcutAdvisor 物件,帶有 @DeclareParents 註解的欄位解析出 IntroductionAdvisor 物件
  2. 篩選出能夠應用於這個 Bean 的 Advisor 們,主要通過 ClassFilter 類過濾器和 MethodMatcher 方法匹配器進行匹配

  3. 對篩選出來的 Advisor 進行擴充套件,例如子類會往首部新增一個 PointcutAdvisor 物件

  4. 對篩選出來的 Advisor 進行排序

    • 不同的 AspectJ 根據 @Order 排序

    • 同一個 AspectJ 中不同 Advisor 的排序,優先順序:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice

AspectJ 中的註解對應的 Advice 型別如下:

  • @Around -> AspectJAroundAdvice,實現了 MethodInterceptor
  • @Before -> AspectJMethodBeforeAdvice
  • @After -> AspectJAfterAdvice,實現了 MethodInterceptor
  • @AfterReturning -> AspectJAfterAdvice
  • @AfterThrowing -> AspectJAfterThrowingAdvice,實現了 MethodInterceptor

好了,本篇文章就到這裡了,如果獲取到能夠應用於某個 Bean 的 Advisor,那麼接下來要做的就是為這個 Bean 建立一個代理物件,通過 JDK 動態代理或者 CGLIB 動態代理,將在下篇文章進行分析。

相關文章