該系列文章是本人在學習 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 進行處理,例如:
-
在 Bean 的例項化前,會呼叫
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation(..)
方法進行處理 -
在 Bean 出現迴圈依賴的情況下,會呼叫
SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference(..)
方法對提前暴露的 Bean 進行處理 -
在 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-aop
和 aspectjweaver
兩個模組,然後通過下面的方式開啟 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 {}
}
只要存在 EnableAspectJAutoProxy
、Aspect
、Advice
、AnnotatedElement
四個 Class 物件,且 spring.aop.auto
配置為 true
(沒有配置則為 true
),那麼就會載入 AopAutoConfiguration 當前這個 Bean,而內部又使用了 @EnableAspectJAutoProxy
註解,那麼表示開啟 AOP。
至於這個註解或者 XML 配置的方式為什麼就開啟 AOP,是因為會引入 AbstractAutoProxyCreator
這個物件,具體怎麼引入的,在後續文章進行分析?
類圖
簡單描述:
- 【重點】
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);
}
該方法的處理過程如下:
- 獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件
- 將當前 Bean 儲存至
earlyProxyReferences
集合(早期的代理應用物件),也就是說當這個 Bean 出現迴圈依賴了,在例項化後就建立了代理物件(如果有必要),可以防止後續初始化後再次建立代理物件 - 【重點】呼叫
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;
}
該方法的處理過程如下:
-
獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件
-
如果沒有
beanName
或者沒有自定義生成 TargetSource- 已建立代理物件(或不需要建立),則直接返回
null
,進行後續的 Bean 載入過程 - 滿足下面其中一個條件表示不需要建立代理物件,則直接返回
null
,並將這個 Bean 不需要建立代理物件的結果儲存至advisedBeans
中- 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記介面)
- 應該跳過
- 已建立代理物件(或不需要建立),則直接返回
-
通過自定義 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; }
-
如果 TargetSource 不為空,表示需要建立代理物件
- 將當前
beanName
儲存至targetSourcedBeans
集合,表示這個 Bean 已自定義生成 TargetSource 物件 - 呼叫
getAdvicesAndAdvisorsForBean(..)
方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序),在後面進行分析?? - 呼叫
createProxy(..)
方法,建立代理物件,JDK 動態代理或者 CGLIB 動態代理,在後面進行分析?? - 將代理物件的 Class 物件(目標類的子類)儲存至
proxyTypes
- 返回代理物件
- 將當前
-
否則,直接返回
null
,進行後續的 Bean 載入過程
這裡先解釋一下 TargetSource,這個物件表示目標類的來源,用於獲取代理物件的目標物件,上面如果存在 TargetSourceCreator,表示可以建立自定義的 TargetSource,也就需要進行 AOP 代理。預設情況下是沒有 TargetSourceCreator 的,具體使用場景目前還沒有接觸過。
上面的 4.2
和 4.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;
}
該方法的處理過程如下:
-
如果
bean
不為空則進行接下來的處理-
獲取這個 Bean 的快取 Key,預設為 Bean 的名稱,沒有則取其對應的 Class 物件
-
移除
earlyProxyReferences
集合中儲存的當前 Bean 物件(如果有的話),如果earlyProxyReferences
集合中沒有當前 Bean 物件,表示在前面沒有建立代理物件【重點】那麼呼叫
wrapIfNecessary(..)
方法,嘗試為這個 Bean 建立一個代理物件(如果有必要的話),該方法在後面分析??
-
-
直接返回
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;
}
該方法處理過程如下:
-
如果當前 Bean 已經建立過自定義 TargetSource 物件,表示在上面的例項化前置處理中已經建立代理物件,那麼直接返回這個物件
-
如果
advisedBeans
儲存了這個 Bean 沒有必要建立代理物件,則直接返回這個物件 -
如果滿足下面其中一個條件,表示不需要建立代理物件,則直接返回當前 Bean,並將這個 Bean 不需要建立代理物件的結果儲存至
advisedBeans
中- 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記接
- 應該跳過
-
呼叫
getAdvicesAndAdvisorsForBean(..)
方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序),在後面進行分析?? -
如果有 Advisor,則進行下面的動態代理建立過程
-
將這個 Bean 已建立代理物件的結果儲存至
advisedBeans
-
呼叫
createProxy(..)
方法,建立代理物件,JDK 動態代理或者 CGLIB 動態代理,在後面進行分析??注意,這裡傳入的是 SingletonTargetSource 物件,可獲取代理物件的目標物件(當前 Bean)
-
將代理物件的 Class 物件(目標類的子類)儲存至
proxyTypes
-
返回代理物件
-
-
否則,將這個 Bean 不需要建立代理物件的結果儲存至
advisedBeans
-
返回這個 Bean 物件
到這裡,getEarlyBeanReference(..)
、postProcessBeforeInstantiation(..)
和 postProcessAfterInitialization(..)
三個方法的 AOP 自動代理過程主要分為兩步:
-
呼叫
getAdvicesAndAdvisorsForBean(..)
方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序) -
呼叫
createProxy(..)
方法,根據找到的 Advisor 建立一個代理物件,JDK 動態代理或者 CGLIB 動態代理
上面的流程看起來並不複雜,看著好像就呼叫兩個方法,不過不要被表象所迷惑,這只是冰山一角。
總結
Spring AOP 自動代理是通過實現 Spring IoC 中的幾種 BeanPostProcessor 處理器,在 Bean 的載入過程中進行擴充套件,如果有必要的話(找到了能夠應用於這個 Bean 的 Advisor)則建立 AOP 代理物件, JDK 動態代理或者 CGLIB 動態代理。
AbstractAutoProxyCreator 則是自動代理的入口,實現了三種 BeanPostProcessor 處理器,SmartInstantiationAwareBeanPostProcessor、InstantiationAwareBeanPostProcessor 和 BeanPostProcessor,實現的三個方法:
-
getEarlyBeanReference(..)
:用於處理早期暴露的物件,如果有必要的話會建立一個代理物件 -
postProcessBeforeInstantiation(..)
:Bean 的建立過程中例項化前置處理,允許你在建立 Bean 之前進行處理。如果該方法返回的不為null
,後續 Bean 載入過程不會繼續,也就是說這個方法可用於獲取一個 Bean 物件。通常這裡用於建立 AOP 代理物件,或者 RPC 遠端呼叫的實現(因為本地類沒有遠端能力,可以通過這種方式進行攔截)。 -
postProcessAfterInitialization(..)
:Bean 的初始化後置處理,在 Bean 初始化後,已經進入一個“成熟態”,那麼此時就可以建立 AOP 代理物件了,如果有必要的話。
詳細過程參考上面的方法,我們主要是通過第 3
種方法建立代理物件,這三種方法的處理過程主要分為以下兩步:
-
呼叫
getAdvicesAndAdvisorsForBean(..)
方法,篩選出合適的 Advisor 物件們 -
呼叫
createProxy(..)
方法,根據找到的 Advisor 建立一個代理物件,JDK 動態代理或者 CGLIB 動態代理
由於這兩個步驟都比較複雜,考慮到文章的可讀性,所以另起兩篇文章分別分析