死磕Spring之AOP篇 - Spring AOP自動代理(一)入口

月圓吖發表於2021-04-19

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

Spring 版本:5.1.14.RELEASE

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

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

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

通過上一篇文章《Spring AOP 總覽》我們對 Spring AOP 有了一個整體的認識,那麼從本篇文章開始,我們一起來看看 Spring AOP 和 Spring IoC 是如何整合的,自動代理的過程做了哪些事情?

首先我們得清楚 Bean 的載入過程,整個過程中會呼叫相應的 BeanPostProcessor 對正在建立 Bean 進行處理,例如:

  1. 在 Bean 的例項化前,會呼叫 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation(..) 方法進行處理

  2. 在 Bean 出現迴圈依賴的情況下,會呼叫 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(..) 方法對提前暴露的 Bean 進行處理

  3. 在 Bean 初始化後,會呼叫 BeanPostProcessor#postProcessAfterInitialization(..) 方法對初始化好的 Bean 進行處理

Spring AOP 則是通過上面三個切入點進行建立代理物件,實現自動代理

  • 在 Spring AOP 中主要是通過第 3 種 BeanPostProcessor 建立代理物件,在 Bean 初始化後,也就是一個“成熟態”,然後再嘗試是否建立一個代理物件;
  • 2 種方式是為了解決 Bean 迴圈依賴的問題,雖然 Bean 僅例項化還未初始化,但是出現了迴圈依賴,不得不在此時建立一個代理物件;
  • 1 種方式是在 Bean 還沒有例項化的時候就提前建立一個代理物件(建立了則不會繼續後續的 Bean 的建立過程),例如 RPC 遠端呼叫的實現,因為本地類沒有遠端能力,可以通過這種方式進行攔截。

對於 Bean 的載入過程不清楚的小夥伴可以檢視我的《死磕Spring之IoC篇 - 文章導讀》這篇文章,或者直接檢視《死磕Spring之IoC篇 - Bean 的建立過程》 這篇文章

Spring AOP 自動代理的實現主要由 AbstractAutoProxyCreator 完成,它實現了 BeanPostProcessor、SmartInstantiationAwareBeanPostProcessor 和 InstantiationAwareBeanPostProcessor 三個介面,那麼這個類就是 Spring AOP 的入口,在這裡將 Advice 織入我們的 Bean 中,建立代理物件。

如何啟用 AOP 模組?

如何開啟 Spring 的 AOP 模組,首先我們需要引入 spring-aopaspectjweaver 兩個模組,然後通過下面的方式開啟 AOP:

  • 新增 @EnableAspectJAutoProxy 註解
  • 新增 <aop:aspectj-autoproxy /> XML 配置

備註:在 Spring Boot 中使用 AOP 可以不需要上面兩種配置,因為在 Spring Boot 中當你引入上面兩個模組後,預設開啟,可以看到下面這個配置類:

package org.springframework.boot.autoconfigure.aop;

@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = false)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
	public static class JdkDynamicAutoProxyConfiguration {}

	@Configuration
	@EnableAspectJAutoProxy(proxyTargetClass = true)
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
	public static class CglibAutoProxyConfiguration {}
}

只要存在 EnableAspectJAutoProxyAspectAdviceAnnotatedElement 四個 Class 物件,且 spring.aop.auto 配置為 true(沒有配置則為 true),那麼就會載入 AopAutoConfiguration 當前這個 Bean,而內部又使用了 @EnableAspectJAutoProxy 註解,那麼表示開啟 AOP。

至於這個註解或者 XML 配置的方式為什麼就開啟 AOP,是因為會引入 AbstractAutoProxyCreator 這個物件,具體怎麼引入的,在後續文章進行分析?

類圖

死磕Spring之AOP篇 - Spring AOP自動代理(一)入口

簡單描述:

  • 【重點】org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator:AOP 自動代理的抽象類,完成主要的邏輯實現,提供一些骨架方法交由子類完成
  • org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator:僅支援指定 List<String> beanNames 完成自動代理,需要指定 interceptorNames 攔截器
  • 【重點】org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator:支援從當前 Spring 上下文獲取所有 Advisor 物件,存在能應用與 Bean 的 Advisor 則建立代理物件
  • org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator:僅支援獲取 Spring 內部的 Advisor 物件(BeanDefinition 的角色為 ROLE_INFRASTRUCTURE
  • org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator:支援配置字首,只能獲取名稱已該字首開頭的 Advisor 物件
  • 【重點】org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator:支援按照 AspectJ 的方式對 Advisor 進行排序
  • 【重點】org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator:支援從帶有 @AspectJ 註解 Bean 中解析 Advisor 物件

我們主要關注上面【重點】的幾個物件,因為 Sping AOP 推薦使用 AspectJ 裡面的註解進行 AOP 的配置,你牢牢記住AbstractAutoProxyCreator這個自動代理類。

AbstractAutoProxyCreator

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator:AOP 自動代理的抽象類,完成主要的邏輯實現,提供一些骨架方法交由子類完成

相關屬性

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

	/** 空物件,表示不需要進行代理 */
	@Nullable
	protected static final Object[] DO_NOT_PROXY = null;

	/**
	 * 空的陣列,表示需要進行代理,但是沒有解析出 Advice
	 * 檢視 {@link BeanNameAutoProxyCreator#getAdvicesAndAdvisorsForBean} 就知道其用途
	 */
	protected static final Object[] PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS = new Object[0];

	/** DefaultAdvisorAdapterRegistry 單例,Advisor介面卡註冊中心 */
	private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();

	/** 是否凍結代理物件 */
	private boolean freezeProxy = false;

	/** 公共的攔截器物件 */
	private String[] interceptorNames = new String[0];

	/** 是否將 `interceptorNames` 攔截器放在最前面 */
	private boolean applyCommonInterceptorsFirst = true;

    /** 自定義的 TargetSource 建立器 */
	@Nullable
	private TargetSourceCreator[] customTargetSourceCreators;

	@Nullable
	private BeanFactory beanFactory;

	/**
	 * 儲存自定義 {@link TargetSource} 物件的 Bean 的名稱
	 */
	private final Set<String> targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

	/**
	 * 儲存提前建立代理物件的 Bean
	 * key:cacheKey(Bean 的名稱或者 Class 物件)
	 * value:Bean 物件
	 *
	 * Spring AOP 的設計之初是讓 Bean 在完全建立好後才完成 AOP 代理,如果出現了迴圈依賴,則需要提前(例項化後還未初始化)建立代理物件
	 * 那麼需要先儲存提前建立代理物件的 Bean,這樣在後面可以防止再次建立代理物件
	 */
	private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

	/**
	 * 儲存代理物件的 Class 物件
	 * key:cacheKey(Bean 的名稱或者 Class 物件)
	 * value:代理物件的 Class 物件(目標類的子類)
	 *
	 */
	private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<>(16);

	/**
	 * 儲存是否需要建立代理物件的資訊
	 * key:cacheKey(Bean 的名稱或者 Class 物件)
	 * value:是否需要建立代理物件,false 表示不需要建立代理物件,true 表示已建立代理物件
	 */
	private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);
}

上面的每個屬性都已經註釋,先簡單理解即可,具體在後面的方法可檢視其用途

getEarlyBeanReference 方法

getEarlyBeanReference(Object bean, String beanName),用於處理早期暴露的物件,如果有必要的話會建立一個代理物件,該方法定義在 SmartInstantiationAwareBeanPostProcessor 中,實現如下:

/**
 * 該方法對早期物件(提前暴露的物件,已例項化還未初始化)進行處理
 * 參考 {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference }
 *
 * @param bean     the raw bean instance
 * @param beanName the name of the bean
 * @return 早期物件(可能是一個代理物件)
 */
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    // <1> 獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    /*
     * <2> 將當前 Bean 儲存至 earlyProxyReferences 集合(早期的代理應用物件)
     * 也就是說當這個 Bean 出現迴圈依賴了,在例項化後就建立了代理物件(如果有必要)
     */
    this.earlyProxyReferences.put(cacheKey, bean);
    // <3> 為這個 Bean 建立代理物件(如果有必要的話)
    return wrapIfNecessary(bean, beanName, cacheKey);
}

該方法的處理過程如下:

  1. 獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件
  2. 將當前 Bean 儲存至 earlyProxyReferences 集合(早期的代理應用物件),也就是說當這個 Bean 出現迴圈依賴了,在例項化後就建立了代理物件(如果有必要),可以防止後續初始化後再次建立代理物件
  3. 【重點】呼叫 wrapIfNecessary(..) 方法,為這個 Bean 建立代理物件(如果有必要的話),該方法在後面分析?‍?

對於 getEarlyBeanReference(..) 方法在哪被呼叫,可能你已經忘記了,這裡來回顧一下:

// 在 AbstractAutowireCapableBeanFactory#doCreateBean 建立 Bean 的過程中,例項化後會執行下面步驟
/**
 * <3.2>
 * 建立一個 ObjectFactory 實現類,用於返回當前正在被建立的 `bean`,提前暴露,儲存在 `singletonFactories` (**三級 Map**)快取中
 *
 * 可以回到前面的 {@link AbstractBeanFactory#doGetBean#getSingleton(String)} 方法
 * 載入 Bean 的過程會先從快取中獲取單例 Bean,可以避免單例模式 Bean 迴圈依賴注入的問題
 */
addSingletonFactory(beanName,
        // ObjectFactory 實現類
        () -> getEarlyBeanReference(beanName, mbd, bean));

// 獲取早期暴露的物件時候的處理,會呼叫 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(..) 方法
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() // RootBeanDefinition 不是使用者定義的(由 Spring 解析出來的)
            && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

上面這個過程是為了處理迴圈依賴的問題,在 Bean 例項化後就提前暴露這個物件,如果真的出現了迴圈依賴,如果這個 Bean 需要進行代理,那麼就不得不提前為它建立一個代理物件,雖然這個 Bean 還未初始化,不是一個“成熟態”。

關於 Spring 如何處理迴圈依賴的過程,如果你不熟悉可檢視我的另一篇文章《死磕Spring之IoC篇 - 單例 Bean 的迴圈依賴處理》

postProcessBeforeInstantiation 方法

postProcessBeforeInstantiation(Class<?> beanClass, String beanName),Bean 的建立過程中例項化前置處理,也就是允許你在建立 Bean 之前進行處理。如果該方法返回的不為 null,後續 Bean 載入過程不會繼續,也就是說這個方法可用於獲取一個 Bean 物件。通常這裡用於建立 AOP 代理物件,或者 RPC 遠端呼叫的實現(因為本地類沒有遠端能力,可以通過這種方式進行攔截)。

該方法定義在 InstantiationAwareBeanPostProcessor 中,實現如下:

/**
 * 在載入 Bean 的過程中,Bean 例項化的前置處理
 * 如果返回的不是 null 則不會進行後續的載入過程,也就是說這個方法用於獲取一個 Bean 物件
 * 通常這裡用於建立 AOP 代理物件,返回的物件不為 null,則會繼續呼叫下面的 {@link this#postProcessAfterInitialization} 方法進行初始化後置處理
 * 參考 {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation}
 *
 * @param beanClass the class of the bean to be instantiated
 * @param beanName  the name of the bean
 * @return 代理物件或者空物件
 */
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    // <1> 獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件
    Object cacheKey = getCacheKey(beanClass, beanName);

    // <2> 如果沒有 beanName 或者沒有自定義生成 TargetSource
    if (!StringUtils.hasLength(beanName) // 沒有 beanName
            || !this.targetSourcedBeans.contains(beanName)) // 沒有自定義生成 TargetSource
    {
        /*
         * <2.1> 已建立代理物件(或不需要建立),則直接返回 null,進行後續的 Bean 載入過程
         */
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        /*
         * <2.2>不需要建立代理物件,則直接返回 null,進行後續的 Bean 載入過程
         */
        if (isInfrastructureClass(beanClass) // 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記介面)
                || shouldSkip(beanClass, beanName)) // 應該跳過
        {
            // 將這個 Bean 不需要建立代理物件的結果儲存起來
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }
    /*
     * <3> 通過自定義 TargetSourceCreator 建立自定義 TargetSource 物件
     * 預設沒有 TargetSourceCreator,所以這裡通常都是返回 null
     */
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    /*
     * <4> 如果 TargetSource 不為空,表示需要建立代理物件
     */
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            // <4.1> 將當前 beanName 儲存至集合,表示這個 Bean 已自定義生成 TargetSource 物件
            this.targetSourcedBeans.add(beanName);
        }
        // <4.2> 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        // <4.3> 建立代理物件,JDK 動態代理或者 CGLIB 動態代理
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        // <4.4> 將代理物件的 Class 物件(目標類的子類)儲存
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // <4.5> 返回代理物件
        return proxy;
    }
    // <5> 否則,直接返回 null,進行後續的 Bean 載入過程
    return null;
}

該方法的處理過程如下:

  1. 獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件

  2. 如果沒有 beanName 或者沒有自定義生成 TargetSource

    1. 已建立代理物件(或不需要建立),則直接返回 null,進行後續的 Bean 載入過程
    2. 滿足下面其中一個條件表示不需要建立代理物件,則直接返回 null,並將這個 Bean 不需要建立代理物件的結果儲存至 advisedBeans
      • 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記介面)
      • 應該跳過
  3. 通過自定義 TargetSourceCreator 建立自定義 TargetSource 物件,預設沒有 TargetSourceCreator,所以這裡通常都是返回 null

    @Nullable
    protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
        if (this.customTargetSourceCreators != null &&
                this.beanFactory != null && this.beanFactory.containsBean(beanName)) {
            for (TargetSourceCreator tsc : this.customTargetSourceCreators) {
                // 通過 TargetSourceCreator 獲取 `beanName` 的自定義 TargetSource
                TargetSource ts = tsc.getTargetSource(beanClass, beanName);
                if (ts != null) {
                    return ts;
                }
            }
        }
        return null;
    }
    
  4. 如果 TargetSource 不為空,表示需要建立代理物件

    1. 將當前 beanName 儲存至 targetSourcedBeans 集合,表示這個 Bean 已自定義生成 TargetSource 物件
    2. 呼叫 getAdvicesAndAdvisorsForBean(..) 方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序),在後面進行分析?‍?
    3. 呼叫 createProxy(..) 方法,建立代理物件,JDK 動態代理或者 CGLIB 動態代理,在後面進行分析?‍?
    4. 將代理物件的 Class 物件(目標類的子類)儲存至 proxyTypes
    5. 返回代理物件
  5. 否則,直接返回 null,進行後續的 Bean 載入過程

這裡先解釋一下 TargetSource,這個物件表示目標類的來源,用於獲取代理物件的目標物件,上面如果存在 TargetSourceCreator,表示可以建立自定義的 TargetSource,也就需要進行 AOP 代理。預設情況下是沒有 TargetSourceCreator 的,具體使用場景目前還沒有接觸過。

上面的 4.24.3 兩個方法非常複雜,放在後面進行分析?

同樣對於 postProcessBeforeInstantiation(..) 方法在哪被呼叫,可能你已經忘記了,這裡來回顧一下:

// 在 AbstractAutowireCapableBeanFactory#createBean 建立 Bean 的過程中,開始前會執行下面步驟
/**
 * <3> 在例項化前進行相關處理,會先呼叫所有 {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation}
 * 注意,如果這裡返回物件不是 `null` 的話,不會繼續往下執行原本初始化操作,直接返回,也就是說這個方法返回的是最終例項物件
 * 可以通過這種方式提前返回一個代理物件,例如 AOP 的實現,或者 RPC 遠端呼叫的實現(因為本地類沒有遠端能力,可以通過這種方式進行攔截)
 */
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
    return bean;
}

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        // 如果 RootBeanDefinition 不是使用者定義的(由 Spring 解析出來的),並且存在 InstantiationAwareBeanPostProcessor 處理器
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                 // 例項化前置處理
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    // 後置處理
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}

上面過程是提供一種擴充套件點,可以讓你在 Bean 建立之前進行相關處理,例如進行 AOP 代理,或者 RPC 遠端呼叫的實現(因為本地類沒有遠端能力,可以通過這種方式進行攔截)。

關於 Spring Bean 的建立過程,如果你不熟悉可檢視我的另一篇文章《死磕Spring之IoC篇 - Bean 的建立過程》

postProcessAfterInitialization 方法

postProcessAfterInitialization(@Nullable Object bean, String beanName),Bean 的初始化後置處理,在 Bean 初始化後,已經進入一個“成熟態”,那麼此時就可以建立 AOP 代理物件了,如果有必要的話。

該方法定義在 BeanPostProcessor 中,實現如下:

/**
 * 在載入 Bean 的過程中,Bean 初始化的後置處理
 * 參考 {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)}
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    // <1> 如果 bean 不為空則進行接下來的處理
    if (bean != null) {
        // <1.1> 獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        /*
         * <1.2> 移除 `earlyProxyReferences` 集合中儲存的當前 Bean 物件(如果有的話)
         * 如果 earlyProxyReferences 集合中沒有當前 Bean 物件,表示在前面沒有建立代理物件
         */
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 這裡嘗試為這個 Bean 建立一個代理物件(如果有必要的話)
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    // <2> 直接返回 bean 物件
    return bean;
}

該方法的處理過程如下:

  1. 如果 bean 不為空則進行接下來的處理

    1. 獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件

    2. 移除 earlyProxyReferences 集合中儲存的當前 Bean 物件(如果有的話),如果 earlyProxyReferences 集合中沒有當前 Bean 物件,表示在前面沒有建立代理物件

      【重點】那麼呼叫 wrapIfNecessary(..) 方法,嘗試為這個 Bean 建立一個代理物件(如果有必要的話),該方法在後面分析?‍?

  2. 直接返回 bean 物件

對於 postProcessAfterInitialization(..) 方法在哪被呼叫,可能你已經忘記了,這裡來回顧一下:

// 在 AbstractAutowireCapableBeanFactory#doCreateBean#initializeBean 建立 Bean 的過程中,屬性填充後會進行初始化,初始化後會執行下面的操作
/**
 * <4> **初始化**階段的**後置處理**,執行所有 BeanPostProcessor 的 postProcessAfterInitialization 方法
 *
 * 在 {@link AbstractApplicationContext#prepareBeanFactory} 方法中會新增 {@link ApplicationListenerDetector} 處理器
 * 如果是單例 Bean 且為 ApplicationListener 型別,則新增到 Spring 應用上下文,和 Spring 事件相關
 */
if (mbd == null || !mbd.isSynthetic()) {
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    // 遍歷所有 BeanPostProcessor
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 初始化的後置處理,返回 `current` 處理結果
        Object current = processor.postProcessAfterInitialization(result, beanName);
        // 處理結果為空,則直接返回 `result`
        if (current == null) {
            return result;
        }
        // 否則,`result` 複製 `current`
        result = current;
    }
    return result;
}

上面這個過程在 Bean 初始化後,提供一個擴充套件點允許對這個 Bean 進行後置處理,此時 Bean 進入一個 “成熟態”,在這裡則可以進行 AOP 代理物件的建立

關於 Spring Bean 的初始化過程,如果你不熟悉可檢視我的另一篇文章《死磕Spring之IoC篇 - Bean 的建立過程》

wrapIfNecessary 方法

wrapIfNecessary(Object bean, String beanName, Object cacheKey),該方法用於建立 AOP 代理物件,如果有必要的話

上面的 getEarlyBeanReference(..)postProcessAfterInitialization(..) 方法都會呼叫這個方法,如下:

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

該方法處理過程如下:

  1. 如果當前 Bean 已經建立過自定義 TargetSource 物件,表示在上面的例項化前置處理中已經建立代理物件,那麼直接返回這個物件

  2. 如果 advisedBeans 儲存了這個 Bean 沒有必要建立代理物件,則直接返回這個物件

  3. 如果滿足下面其中一個條件,表示不需要建立代理物件,則直接返回當前 Bean,並將這個 Bean 不需要建立代理物件的結果儲存至 advisedBeans

    • 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記接
    • 應該跳過
  4. 呼叫 getAdvicesAndAdvisorsForBean(..) 方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序),在後面進行分析?‍?

  5. 如果有 Advisor,則進行下面的動態代理建立過程

    1. 將這個 Bean 已建立代理物件的結果儲存至 advisedBeans

    2. 呼叫 createProxy(..) 方法,建立代理物件,JDK 動態代理或者 CGLIB 動態代理,在後面進行分析?‍?

      注意,這裡傳入的是 SingletonTargetSource 物件,可獲取代理物件的目標物件(當前 Bean)

    3. 將代理物件的 Class 物件(目標類的子類)儲存至 proxyTypes

    4. 返回代理物件

  6. 否則,將這個 Bean 不需要建立代理物件的結果儲存至 advisedBeans

  7. 返回這個 Bean 物件

到這裡,getEarlyBeanReference(..)postProcessBeforeInstantiation(..)postProcessAfterInitialization(..) 三個方法的 AOP 自動代理過程主要分為兩步:

  1. 呼叫 getAdvicesAndAdvisorsForBean(..) 方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)

  2. 呼叫 createProxy(..) 方法,根據找到的 Advisor 建立一個代理物件,JDK 動態代理或者 CGLIB 動態代理

上面的流程看起來並不複雜,看著好像就呼叫兩個方法,不過不要被表象所迷惑,這只是冰山一角。

AOP

總結

Spring AOP 自動代理是通過實現 Spring IoC 中的幾種 BeanPostProcessor 處理器,在 Bean 的載入過程中進行擴充套件,如果有必要的話(找到了能夠應用於這個 Bean 的 Advisor)則建立 AOP 代理物件, JDK 動態代理或者 CGLIB 動態代理。

AbstractAutoProxyCreator 則是自動代理的入口,實現了三種 BeanPostProcessor 處理器,SmartInstantiationAwareBeanPostProcessor、InstantiationAwareBeanPostProcessor 和 BeanPostProcessor,實現的三個方法:

  1. getEarlyBeanReference(..):用於處理早期暴露的物件,如果有必要的話會建立一個代理物件

  2. postProcessBeforeInstantiation(..):Bean 的建立過程中例項化前置處理,允許你在建立 Bean 之前進行處理。如果該方法返回的不為 null,後續 Bean 載入過程不會繼續,也就是說這個方法可用於獲取一個 Bean 物件。通常這裡用於建立 AOP 代理物件,或者 RPC 遠端呼叫的實現(因為本地類沒有遠端能力,可以通過這種方式進行攔截)。

  3. postProcessAfterInitialization(..):Bean 的初始化後置處理,在 Bean 初始化後,已經進入一個“成熟態”,那麼此時就可以建立 AOP 代理物件了,如果有必要的話。

詳細過程參考上面的方法,我們主要是通過第 3 種方法建立代理物件,這三種方法的處理過程主要分為以下兩步:

  1. 呼叫 getAdvicesAndAdvisorsForBean(..) 方法,篩選出合適的 Advisor 物件們

  2. 呼叫 createProxy(..) 方法,根據找到的 Advisor 建立一個代理物件,JDK 動態代理或者 CGLIB 動態代理

由於這兩個步驟都比較複雜,考慮到文章的可讀性,所以另起兩篇文章分別分析

相關文章