該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 Spring 原始碼分析 GitHub 地址 進行閱讀。
Spring 版本:5.1.14.RELEASE
在開始閱讀 Spring AOP 原始碼之前,需要對 Spring IoC 有一定的瞭解,可檢視我的 《死磕Spring之IoC篇 - 文章導讀》 這一系列文章
瞭解 AOP 相關術語,可先檢視 《Spring AOP 常見面試題) 》 這篇文章
該系列其他文章請檢視:《死磕 Spring 之 AOP 篇 - 文章導讀》
在前面的《Spring AOP 自動代理(一)入口》文章中,分析了 Spring AOP 自動代理的入口是 AbstractAutoProxyCreator 物件,其中自動代理的過程主要分為下面兩步:
- 篩選出能夠應用於當前 Bean 的 Advisor
- 找到了合適 Advisor 則建立一個代理物件, JDK 動態代理或者 CGLIB 動態代理
上一篇《Spring AOP 自動代理(二)篩選合適的通知器》文章分析了上面第 1
步的處理過程,先去解析出當前 IoC 容器所有 Advisor 物件,包括 Advisor 型別的 Bean 和從 @AspectJ
註解的 Bean 解析出來的 Advisor 物件;然後通過通過 ClassFilter 類過濾器和 MethodMatcher 方法匹配器篩選出能夠應用於這個 Bean 的 Advisor 們,最後進行排序;不同的 AspectJ 根據 @Order
排序,同一個 AspectJ 中不同 Advisor 的排序,優先順序:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice
本文將會分析上面的第 2
的建立過程,如果這個 Bean 有合適的 Advisor,那麼為這個 Bean 建立一個代理物件,我們一起來看看是如何建立代理物件的。
回顧
// 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
步嘗試獲取能夠應用於當前 Bean 的 Advisor,該過程在上一篇文章中進行分析過,如果有 Advisor,則呼叫 createProxy(..)
方法建立代理物件
注意,這裡傳入的 TargetSource 是一個 SingletonTargetSource 物件,可獲取目標物件,如下:
public class SingletonTargetSource implements TargetSource, Serializable {
private static final long serialVersionUID = 9031246629662423738L;
private final Object target;
public SingletonTargetSource(Object target) {
Assert.notNull(target, "Target object must not be null");
this.target = target;
}
@Override
public Class<?> getTargetClass() {
return this.target.getClass();
}
@Override
public Object getTarget() {
return this.target;
}
@Override
public void releaseTarget(Object target) {
// nothing to do
}
@Override
public boolean isStatic() {
return true;
}
}
建立代理物件的流程
-
建立一個 ProxyFactory 代理工廠物件,設定需要建立的代理類的配置資訊,例如 Advisor 陣列和 TargetSource 目標類來源
-
藉助 DefaultAopProxyFactory 選擇 JdkDynamicAopProxy(JDK 動態代理)還是 ObjenesisCglibAopProxy(CGLIB 動態代理)
-
當
proxy-target-class
為false
時,優先使用 JDK 動態代理,如果目標類沒有實現可代理的介面,那麼還是使用 CGLIB 動態代理 -
如果為
true
,優先使用 CGLIB 動態代理,如果目標類本身是一個介面,那麼還是使用 JDK 動態代理
-
-
通過 JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy 建立一個代理物件
- JdkDynamicAopProxy 本身是一個 InvocationHandler 實現類,通過 JDK 的
Proxy.newProxyInstance(..)
建立代理物件 - ObjenesisCglibAopProxy 藉助 CGLIB 的 Enhancer 建立代理物件,會設定 Callback 陣列和 CallbackFilter 篩選器(選擇合適 Callback 處理對應的方法),整個過程相比於 JDK 動態代理更復雜點,主要的實現在 DynamicAdvisedInterceptor 方法攔截器中
- JdkDynamicAopProxy 本身是一個 InvocationHandler 實現類,通過 JDK 的
AbstractAutoProxyCreator
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
:AOP 自動代理的抽象類,完成主要的邏輯實現,提供一些骨架方法交由子類完成
1. createProxy 方法
createProxy(..)
方法,為目標物件建立一個代理物件,如下:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
// 為目標 Bean 的 BeanDefinition 物件設定一個屬性
// org.springframework.aop.framework.autoproxy.AutoProxyUtils.originalTargetClass -> 目標 Bean 的 Class 物件
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// <1> 建立一個代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
// <2> 複製當前 ProxyConfig 的一些屬性(例如 proxyTargetClass、exposeProxy)
proxyFactory.copyFrom(this);
/**
* <3> 判斷是否類代理,也就是是否開啟 CGLIB 代理
* 預設配置下為 `false`,參考 {@link org.springframework.context.annotation.EnableAspectJAutoProxy}
*/
if (!proxyFactory.isProxyTargetClass()) {
/*
* <3.1> 如果這個 Bean 配置了進行類代理,則設定為 `proxyTargetClass` 為 `true`
*/
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
/*
* <3.2> 檢測當前 Bean 實現的介面是否包含可代理的介面
* 如沒有實現,則將 `proxyTargetClass` 設為 `true`,表示需要進行 CGLIB 提升
*/
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
/*
* <4> 對入參的 Advisor 進一步處理,因為其中可能還存在 Advice 型別,需要將他們包裝成 DefaultPointcutAdvisor 物件
* 如果配置了 `interceptorNames` 攔截器,也會新增進來
*/
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// <5> 代理工廠新增 Advisor 陣列
proxyFactory.addAdvisors(advisors);
// <6> 代理工廠設定 TargetSource 物件
proxyFactory.setTargetSource(targetSource);
// <7> 對 ProxyFactory 進行加工處理,抽象方法,目前沒有子類實現
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
// <8> 是否這個 AdvisedSupport 配置管理器已經過濾過目標類(預設為 false)
if (advisorsPreFiltered()) {
// 設定 `preFiltered` 為 `true`
// 這樣 Advisor 們就不會根據 ClassFilter 進行過濾了,而直接通過 MethodMatcher 判斷是否處理被攔截方法
proxyFactory.setPreFiltered(true);
}
// <9> 通過 ProxyFactory 代理工廠建立代理物件
return proxyFactory.getProxy(getProxyClassLoader());
}
該方法的處理過程如下:
- 建立一個 ProxyFactory 代理工廠
proxyFactory
- 複製當前物件的一些屬性給
proxyFactory
(例如 proxyTargetClass、exposeProxy),當前 AbstractAutoProxyCreator 物件繼承了 ProxyConfig - 判斷是否類代理,也就是是否開啟 CGLIB 代理,預設配置下為
false
,如果沒有的話,進行下面處理- 如果這個 Bean 配置了進行類代理,則設定為
proxyTargetClass
為true
- 否則,檢測當前 Bean 實現的介面是否包含可代理的介面,如沒有實現,則將
proxyTargetClass
設為true
,表示需要進行 CGLIB 提升
- 如果這個 Bean 配置了進行類代理,則設定為
- 呼叫
buildAdvisors(..)
方法,對入參的 Advisor 陣列進一步處理,會將不是 Advisor 型別的物件包裝成 DefaultPointcutAdvisor 物件 proxyFactory
代理工廠新增 Advisor 陣列proxyFactory
代理工廠設定 TargetSource 物件,使用者獲取目標物件- 對
proxyFactory
進行加工處理,抽象方法,目前沒有子類實現 - 是否這個 AdvisedSupport 配置管理器已經過濾過目標類(預設為
false
)- 是的話設定
preFiltered
為true
,這樣 Advisor 們就不會根據 ClassFilter 進行過濾了,而直接通過 MethodMatcher 判斷是否處理被攔截方法
- 是的話設定
- 呼叫
proxyFactory
代理工廠的getProxy(@Nullable ClassLoader classLoader)
方法建立代理物件
這個過程不復雜,容易理解,其中第 4
步會再次對 Advisor 陣列進一步處理,在第 9
步根據 proxyFactory
建立代理物件
buildAdvisors 方法
buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors)
方法,對 Advisor 陣列進一步處理,如下:
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
// Handle prototypes correctly...
// <1> 將配置的 `interceptorNames` 轉換成 Advisor 型別(預設沒有)
Advisor[] commonInterceptors = resolveInterceptorNames();
// <2> 將 commonInterceptors 與 specificInterceptors 放入一個集合
List<Object> allInterceptors = new ArrayList<>();
if (specificInterceptors != null) {
allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (commonInterceptors.length > 0) {
// 是否新增至最前面(預設為 true)
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
else {
allInterceptors.addAll(Arrays.asList(commonInterceptors));
}
}
}
Advisor[] advisors = new Advisor[allInterceptors.size()];
/*
* <3> 遍歷 `specificInterceptors` 陣列
*/
for (int i = 0; i < allInterceptors.size(); i++) {
// <3.1> 將不是 Advisor 型別的 Advice 或者 MethodInterceptor 包裝成 DefaultPointcutAdvisor 物件
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
// <4> 返回構建好的 Advisor 陣列
return advisors;
}
該方法的處理過程如下:
-
將配置的
interceptorNames
轉換成 Advisor 型別(預設沒有),得到commonInterceptors
陣列,如下:private Advisor[] resolveInterceptorNames() { BeanFactory bf = this.beanFactory; ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null); List<Advisor> advisors = new ArrayList<>(); for (String beanName : this.interceptorNames) { if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) { Assert.state(bf != null, "BeanFactory required for resolving interceptor names"); Object next = bf.getBean(beanName); advisors.add(this.advisorAdapterRegistry.wrap(next)); } } return advisors.toArray(new Advisor[0]); }
解析
interceptorNames
中對應的 Bean,幷包裝成 Advisor 型別(如果需要的話) -
將上一步獲取到的
commonInterceptors
陣列放入入參中的specificInterceptors
陣列中 -
遍歷
specificInterceptors
陣列-
將不是 Advisor 型別的 Advice 或者 MethodInterceptor 包裝成 DefaultPointcutAdvisor 物件
// DefaultAdvisorAdapterRegistry.java @Override public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException { if (adviceObject instanceof Advisor) { // Advisor 型別,直接返回 return (Advisor) adviceObject; } if (!(adviceObject instanceof Advice)) { // 非 Advice 介面,丟擲異常 throw new UnknownAdviceTypeException(adviceObject); } Advice advice = (Advice) adviceObject; if (advice instanceof MethodInterceptor) { // MethodInterceptor 型別,包裝成 DefaultPointcutAdvisor 物件 // So well-known it doesn't even need an adapter. return new DefaultPointcutAdvisor(advice); } for (AdvisorAdapter adapter : this.adapters) { // Check that it is supported. // 檢查該 Advice 型別是否支援 if (adapter.supportsAdvice(advice)) { // 包裝成 DefaultPointcutAdvisor 物件 返回 return new DefaultPointcutAdvisor(advice); } } throw new UnknownAdviceTypeException(advice); }
-
-
返回構建好的 Advisor 陣列
能夠應用於這個 Bean 的 Advisor 們已經準備好了,那麼接下來我們來看看 ProxyFactory 是如何建立代理物件的
2. ProxyFactory
org.springframework.aop.framework.ProxyFactory
,代理工廠,如下:
public Object getProxy(@Nullable ClassLoader classLoader) {
// <1> 先建立一個 AOP 代理類(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy)
// <2> 根據 AOP 代理為目標 Bean 建立一個代理物件,並返回
return createAopProxy().getProxy(classLoader);
}
過程分為兩步:
-
呼叫
createAopProxy()
方法,建立一個 AOP 代理類(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy),如下:protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } // <1> 先獲取 AOP 代理工廠,預設為 DefaultAopProxyFactory,只有這個實現 // <2> 然後通過它根據建立當前 AdvisedSupport 配置管理器建立一個 AOP 代理(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy) return getAopProxyFactory().createAopProxy(this); }
-
然後呼叫 AOP 代理類的
getProxy(@Nullable ClassLoader classLoader)
方法獲取代理物件
可以看到選擇 JDK 動態代理還是選擇 CGLIB 動態代理在 DefaultAopProxyFactory 中可以找到答案
3. DefaultAopProxyFactory
org.springframework.aop.framework.DefaultAopProxyFactory
,預設的 AOP 代理工廠,如下:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
/*
* <1> 判斷是否滿足下面三個條件的其中一個
*/
if (config.isOptimize() // 需要優化,預設為 `false`
|| config.isProxyTargetClass() // 使用類代理,也就是使用 CGLIB 動態代理
|| hasNoUserSuppliedProxyInterfaces(config) // 目標類沒有實現介面
) {
// <1.1> 獲取目標類
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
/*
* <1.2> 如果目標類是一個介面或者是 java.lang.reflect.Proxy 的子類
* 則還是使用 JDK 動態代理,建立一個 JdkDynamicAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
*/
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// <1.3> 使用 CGLIB 動態代理,建立一個 ObjenesisCglibAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
return new ObjenesisCglibAopProxy(config);
}
// <2> 否則
else {
// 使用 JDK 動態代理,建立一個 JdkDynamicAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
return new JdkDynamicAopProxy(config);
}
}
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
建立 AOP 代理類的過程如下:
-
判斷是否滿足下面三個條件的其中一個,則進行下面的處理
需要優化,預設為
false
;使用類代理,也就是使用 CGLIB 動態代理,在前面的
AbstractAutoProxyCreator#createProxy(..)
方法中有提到過;目標類沒有實現介面;
- 獲取目標類
- 如果目標類是一個介面或者是
java.lang.reflect.Proxy
的子類,則還是使用JDK 動態代理,建立一個JdkDynamicAopProxy
物件 - 否則,使用 CGLIB 動態代理,建立一個
ObjenesisCglibAopProxy
物件
-
否則,使用JDK 動態代理,建立一個
JdkDynamicAopProxy
物件
我們可以看到JDK 動態代理對應 JdkDynamicAopProxy
物件,CGLIB 動態代理對應 ObjenesisCglibAopProxy
(繼承 CglibAopProxy
)物件,在建立這兩個物件的時候都傳入了一個引數,就是 AdvisedSupport
配置管理器物件。
回到前面的 ProxyFactory#createAopProxy()
方法中,這個 AdvisedSupport
物件就是這個 ProxyFactory
物件,它繼承了 AdvisedSupport
。
再回到 AbstractAutoProxyCreator#createProxy(..)
方法中,這個 ProxyFactory
物件是在這建立的,包含了 TargetSource
目標類來源和能夠應用於當前目標物件的所有 Advisor
。
所以,我們得到的JdkDynamicAopProxy
或者ObjenesisCglibAopProxy
都包含了建立 AOP 代理物件的所有配置資訊,可以建立代理物件了。
通過上面的這個方法,我們還可以得出一個結論,關於
proxy-target-class
配置的含義是什麼?首先這個配置表示是否進行類代理,也就是 CGLIB 動態代理,預設是
false
當為
false
時,優先使用 JDK 動態代理,如果目標類沒有實現可代理的介面,那麼還是使用 CGLIB 動態代理如果為
true
,優先使用 CGLIB 動態代理,如果目標類本身是一個介面,那麼還是使用 JDK 動態代理
到這裡,我們知道了使用哪種方式建立代理物件,那麼接下里我們一起來看看兩種方式建立代理物件的過程。
4. JdkDynamicAopProxy
org.springframework.aop.framework.JdkDynamicAopProxy
,JDK 動態代理類,實現了 InvocationHandler 介面,可建立代理物件
建構函式
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
/** use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 5531744639992436476L;
/** We use a static Log to avoid serialization issues. */
private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
/** 代理物件的配置資訊,例如儲存了 TargetSource 目標類來源、能夠應用於目標類的所有 Advisor */
private final AdvisedSupport advised;
/** 目標物件是否重寫了 equals 方法 */
private boolean equalsDefined;
/** 目標物件是否重寫了 hashCode 方法 */
private boolean hashCodeDefined;
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 // 沒有 Advisor,表示沒有任何動作
&& config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) // 沒有來源
{
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
}
4.1 getProxy 方法
getProxy()
方法,獲取一個 JDK 動態代理物件,如下:
@Override
public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); }
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
// <1> 獲取需要代理的介面(目標類實現的介面,會加上 Spring 內部的幾個介面,例如 SpringProxy)
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
// <2> 判斷目標類是否重寫了 `equals` 或者 `hashCode` 方法
// 沒有重寫在攔截到這兩個方法的時候,會呼叫當前類的實現
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// <3> 呼叫 JDK 的 Proxy#newProxyInstance(..) 方法建立代理物件
// 傳入的引數就是當前 ClassLoader 類載入器、需要代理的介面、InvocationHandler 實現類
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
獲取代理物件的過程如下:
-
獲取需要代理的介面(目標類實現的介面,會加上 Spring 內部的幾個介面,例如 SpringProxy)
-
判斷目標類是否重寫了
equals
或者hashCode
方法,沒有重寫在攔截到這兩個方法的時候,會呼叫當前類的實現private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) { for (Class<?> proxiedInterface : proxiedInterfaces) { Method[] methods = proxiedInterface.getDeclaredMethods(); for (Method method : methods) { if (AopUtils.isEqualsMethod(method)) { this.equalsDefined = true; } if (AopUtils.isHashCodeMethod(method)) { this.hashCodeDefined = true; } if (this.equalsDefined && this.hashCodeDefined) { return; } } } }
-
呼叫 JDK 的
Proxy#newProxyInstance(..)
方法建立代理物件,入參的 InvocationHandler 實現了就是當前物件
對於上面 JDK 建立代理物件的過程是不是很熟悉,不清楚的小夥伴可以檢視我前面 《初識 JDK、CGLIB 兩種動態代理》 這篇文章
AopProxyUtils
org.springframework.aop.framework.AopProxyUtils
工具類,獲取 JDK 建立代理物件時需要實現哪些介面,如下:
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
// <1> 獲取需要代理的介面(目標類實現的介面),放入 `specifiedInterfaces` 中
Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
if (specifiedInterfaces.length == 0) {
// No user-specified interfaces: check whether target class is an interface.
Class<?> targetClass = advised.getTargetClass();
if (targetClass != null) {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
}
}
// <2> 判斷目標類實現的介面是否存在 SpringProxy|Advised|DecoratingProxy 介面,不存在需要新增一個
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
int nonUserIfcCount = 0;
if (addSpringProxy) {
nonUserIfcCount++;
}
if (addAdvised) {
nonUserIfcCount++;
}
if (addDecoratingProxy) {
nonUserIfcCount++;
}
Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
int index = specifiedInterfaces.length;
// <3> 如果目標類實現的介面沒有 SpringProxy,則新增一個
// 這樣建立的代理物件就會實現這個介面,就能知道這個代理物件是否由 Spring 建立
if (addSpringProxy) {
proxiedInterfaces[index] = SpringProxy.class;
index++;
}
// 新增一個 Advised 介面
if (addAdvised) {
proxiedInterfaces[index] = Advised.class;
index++;
}
// 新增一個 DecoratingProxy 介面
if (addDecoratingProxy) {
proxiedInterfaces[index] = DecoratingProxy.class;
}
// <4> 返回需要代理的介面
return proxiedInterfaces;
}
該方法的處理過程如下:
- 獲取需要代理的介面(目標類實現的介面),放入
specifiedInterfaces
中 - 判斷目標類實現的介面是否存在 SpringProxy|Advised|DecoratingProxy 介面,不存在需要新增一個
- 通常情況下,上面三個介面都會新增到
specifiedInterfaces
的後面 - 返回
specifiedInterfaces
陣列
通過上面這個方法得到的結論:在 Spring 內部通過 JDK 建立的代理物件會實現額外的介面,包括 SpringProxy 標記介面,可區分代理物件是否為 Spring 建立的
4 CglibAopProxy
org.springframework.aop.framework.CglibAopProxy
,CGLIB 動態代理類,可建立代理物件
CGLIB 動態代理比 JDK 動態代理可要複雜得多~
建構函式
class CglibAopProxy implements AopProxy, Serializable {
// Constants for CGLIB callback array indices
// 因為 CGLIB 設定的 Callback 是一個陣列,下面定義了陣列中固定幾個攔截器的位置
// 進行 AOP 代理的通用攔截器
private static final int AOP_PROXY = 0;
// 執行目標方法的攔截器
private static final int INVOKE_TARGET = 1;
// 空的 Callback 物件,對於 finalize() 方法,不需要進行任何處理
private static final int NO_OVERRIDE = 2;
// 目標物件排程器,用於獲取目標物件
private static final int DISPATCH_TARGET = 3;
// 配置管理器的排程器,會返回一個 AdvisedSupport 物件
private static final int DISPATCH_ADVISED = 4;
// 處理 `equals(Object)` 方法的攔截器
private static final int INVOKE_EQUALS = 5;
// 處理 `hashCode()` 方法的攔截器
private static final int INVOKE_HASHCODE = 6;
/** Logger available to subclasses; static to optimize serialization. */
protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);
/** Keeps track of the Classes that we have validated for final methods. */
private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<>();
/** 代理物件的配置資訊,例如儲存了 TargetSource 目標類來源、能夠應用於目標類的所有 Advisor */
protected final AdvisedSupport advised;
/** 建立代理物件的構造方法入參 */
@Nullable
protected Object[] constructorArgs;
/** 建立代理物件的構造方法的入參型別 */
@Nullable
protected Class<?>[] constructorArgTypes;
/** Dispatcher used for methods on Advised. 配置管理器的排程器,會返回一個 AdvisedSupport 物件 */
private final transient AdvisedDispatcher advisedDispatcher;
/**
* 快取方法的呼叫器陣列索引
* key:方法名稱
* value:方法對應的方法呼叫器在 Callback 陣列中的位置
*/
private transient Map<String, Integer> fixedInterceptorMap = Collections.emptyMap();
// 方法呼叫器在 Callback 陣列中的偏移量
private transient int fixedInterceptorOffset;
public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
if (config.getAdvisors().length == 0 // 沒有 Advisor,表示沒有任何動作
&& config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) // 沒有來源
{
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
this.advisedDispatcher = new AdvisedDispatcher(this.advised);
}
}
上面的每一個屬性都有註釋,都非常重要,在 Spring 的 CGLIB 動態代理建立代理物件時,會設定一個 Callback 陣列,上面定義了陣列中固定幾個攔截器的位置
陣列中前面的幾個 Callback 如下:
0
:進行 AOP 代理的通用攔截器【重點關注】1
:執行目標方法的攔截器2
:空的 Callback 物件,例如finalize()
方法,不需要進行任何處理3
:目標物件排程器,用於獲取目標物件4
:配置管理器的排程器,會返回一個 AdvisedSupport 物件5
:處理equals(Object)
方法的攔截器6
:處理hashCode()
方法的攔截器
4.1 getProxy 方法
getProxy()
方法,獲取一個 CGLIB 動態代理物件,如下:
@Override
public Object getProxy() { return getProxy(null); }
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
try {
// <1> 獲取目標類
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
// <2> 將目標類作為被代理的類
Class<?> proxySuperClass = rootClass;
// <3> 如果目標類已經被 CGLIB 提升(名稱包含 `$$` 符號)
if (ClassUtils.isCglibProxyClass(rootClass)) {
// <3.1> 獲取目標類的父類作為被代理的類
proxySuperClass = rootClass.getSuperclass();
// <3.2> 獲取目標類實現的介面,並新增至當前 AdvisedSupport 配置管理器中
// 例如 `@Configuration` 註解的 Bean 會被 CGLIB 提升,實現了 EnhancedConfiguration 介面
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
// <4> 進行校驗,僅列印日誌,例如 final 修飾的方法不能被 CGLIB 提升
validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer...
// <5> 建立 CGLIB 的增強類,並進行接下來的配置
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
// `useCache` 預設為 `true`,可通過 `cglib.useCache` 系統引數指定
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
// <5.1> 設定被代理的類
enhancer.setSuperclass(proxySuperClass);
// <5.2> 設定需要代理的介面(可能沒有,不過都會加上 Spring 內部的幾個介面,例如 SpringProxy)
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
// <5.3> 設定命名策略,預設生成的代理物件的名稱中包含 '$$' 和 'BySpringCGLIB'
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
// <5.4> 獲取回撥介面,也就是 MethodInterceptor 方法攔截器
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
// <5.5> 設定 Callback 過濾器,用於篩選出方法對應的 Callback 回撥介面
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
// <6> 建立代理物件,CGLIG 位元組碼替身,建立目標類的子類
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
// 丟擲 AopConfigException 異常
}
catch (Throwable ex) {
// 丟擲 AopConfigException 異常
}
}
該方法的處理過程如下:
- 獲取目標類
rootClass
- 將目標類作為被代理的類
proxySuperClass
- 如果目標類已經被 CGLIB 提升(名稱包含
$$
符號)- 獲取目標類的父類作為被代理的類
- 獲取目標類實現的介面,並新增至當前 AdvisedSupport 配置管理器中,例如
@Configuration
註解的 Bean 會被 CGLIB 提升,實現了 EnhancedConfiguration 介面
- 進行校驗,僅列印日誌,例如 final 修飾的方法不能被 CGLIB 提升
- 建立 CGLIB 的增強類 Enhancer 物件,並進行接下來的配置
- 設定被代理的類為
proxySuperClass
- 設定需要代理的介面(例如 SpringProxy),在前面講述 JDK 動態代理建立代理物件時已經講過 AopProxyUtils 這個工具類,不同的是 CGLIB 不會新增 DecoratingProxy 這個介面
- 設定命名策略,預設生成的代理物件的名稱中包含
$$
和BySpringCGLIB
- 呼叫
getCallbacks(Class<?>)
方法,獲取回撥陣列Callback[] callbacks
,也就是 MethodInterceptor 方法攔截器 - 設定 Callback 過濾器為
ProxyCallbackFilter
,用於篩選出方法使用callbacks
中的哪個 Callback
- 設定被代理的類為
- 呼叫
createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks)
方法,建立代理物件,並返回
CGLIB 建立代理物件的過程整體上並不複雜,其中第 5.4
、5.5
和 6
步我們接下來逐步分析
4.2 getCallbacks 方法
getCallbacks(Class<?> rootClass)
方法,獲取代理物件的 Callback 回撥陣列,如下:
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
// <1> 獲取代理物件的三個配置資訊
// `exposeProxy` 是否暴露代理物件,通常為 `false`
boolean exposeProxy = this.advised.isExposeProxy();
// `isFrozen` 配置管理器是否被凍結,通常為 `false`
boolean isFrozen = this.advised.isFrozen();
// `isStatic` 是否是靜態的目標物件,也就是說目標物件是否每次都需要建立,通常為 `true`
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
// <2> 【重點】建立目標物件的攔截器,MethodInterceptor 方法攔截器,會對目標物件的方法進行攔截處理,之前篩選出來的 Advisor 會在這個裡面被呼叫
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy.
// <3> 建立目標物件的執行器,用於執行目標方法
Callback targetInterceptor;
// <3.1> 如果需要暴露當前代理物件,則通過 AopContext 進行暴露,放入 ThreadLocal 中,其他的和下面都相同
if (exposeProxy) {
targetInterceptor = (isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
}
// <3.2> 否則,不暴露
else {
targetInterceptor = (isStatic ?
// <3.2.1> 用於執行方法代理物件,並對最終的返回結果進一步處理(返回結果是否需要為代理物件,返回結果是否不能為空)
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
// <3.2.2> 和上者的區別就是,每次攔截都會重新獲取目標物件,結束後釋放該目標物件
new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
}
// Choose a "direct to target" dispatcher (used for
// unadvised calls to static targets that cannot return this).
// <4> 目標物件排程器,用於獲取目標物件
Callback targetDispatcher = (isStatic ?
// <4.1> 目標物件的排程器,用於獲取當前目標物件
new StaticDispatcher(this.advised.getTargetSource().getTarget()) :
// <4.2> 目標物件的排程器,空的 Callback,不做任何處理
new SerializableNoOp());
// <5> 生成主要的幾個回撥介面,也就是上面建立的幾個 Callback,放入 `mainCallbacks` 陣列
Callback[] mainCallbacks = new Callback[] {
// 0:進行 AOP 代理的通用攔截器
aopInterceptor, // for normal advice
// 1:執行目標方法的攔截器
targetInterceptor, // invoke target without considering advice, if optimized
// 2:空的 Callback 物件,例如 `finalize()` 方法,不需要進行任何處理
new SerializableNoOp(), // no override for methods mapped to this
// 3:目標物件排程器,用於獲取目標物件
targetDispatcher,
// 4:配置管理器的排程器,會返回一個 AdvisedSupport 物件
// 因為代理物件會實現 Advised 介面,攔截到其裡面的方法時,需要呼叫當前 AdvisedSupport 的方法
this.advisedDispatcher,
// 5:處理 `equals(Object)` 方法的攔截器
new EqualsInterceptor(this.advised),
// 6:處理 `hashCode()` 方法的攔截器
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
// If the target is a static one and the advice chain is frozen,
// then we can make some optimizations by sending the AOP calls
// direct to the target using the fixed chain for that method.
/*
* <6> 如果目標物件不需要每次都建立,且當前 AdvisedSupport 配置管理器被凍結了,效能優化,可暫時忽略
* 那麼在建立代理物件的時候,就可以先將目標物件的每個方法對應的方法呼叫器解析出來,該過程有點效能損耗,這樣在代理物件執行方法的時候效能有所提升
* 不過由於這裡會解析出許多方法呼叫器,會佔有一定的記憶體,以空間換時間
*/
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap<>(methods.length);
// TODO: small memory optimization here (can skip creation for methods with no advice)
/*
* <6.1> 遍歷目標物件所有的方法
*/
for (int x = 0; x < methods.length; x++) {
Method method = methods[x];
// <6.1.1> 獲取能夠應用於該方法的所有攔截器(有序)
// 不同的 AspectJ 根據 @Order 排序
// 同一個 AspectJ 中的 Advice 排序:AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
// <6.1.2> 建立一個方法呼叫器
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
// <6.1.3> 將每個方法的對應的呼叫器的位置索引快取起來
this.fixedInterceptorMap.put(methods.toString(), x);
}
// Now copy both the callbacks from mainCallbacks
// and fixedCallbacks into the callbacks array.
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
// <6.2> 將 `mainCallbacks` 複製到 `callbacks` 陣列
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
// <6.3> 將 `fixedCallbacks` 複製到 `callbacks` 陣列(拼接在後面)
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
// <6.4> 記錄一個已生成的方法呼叫器的偏移量
this.fixedInterceptorOffset = mainCallbacks.length;
}
// <7> 否則,不進行解析,取上面的幾個主要的 Callback
else {
callbacks = mainCallbacks;
}
// <8> 返回這個目標物件對應的 Callback 陣列
return callbacks;
}
該方法的處理過程如下:
-
是獲取代理物件的三個配置資訊
exposeProxy
是否暴露代理物件,通常為false
isFrozen
配置管理器是否被凍結,通常為false
isStatic
是否是靜態的目標物件,也就是說目標物件是否每次都需要建立,通常為true
-
【重點】建立目標物件的攔截器,DynamicAdvisedInterceptor 方法攔截器,會對目標物件的方法進行攔截處理,代理物件的處理邏輯都在這裡面完成
-
建立目標物件的執行器,用於執行目標方法
- 如果需要暴露當前代理物件,則通過 AopContext 進行暴露,放入 ThreadLocal 中,其他的和下面都相同
- 否則,不暴露
isStatic
為 true,建立 StaticUnadvisedInterceptor 物件,用於執行方法代理物件,並對最終的返回結果進一步處理(返回結果是否需要為代理物件,返回結果是否不能為空)- 否則,建立 DynamicUnadvisedInterceptor 物件,和上者的區別就是,每次攔截都會重新獲取目標物件,結束後釋放該目標物件
-
目標物件排程器,用於獲取目標物件
isStatic
為 true,建立 StaticDispatcher 物件,目標物件的排程器,用於獲取當前目標物件- 否則,建立 SerializableNoOp 物件,空的 Callback,不做任何處理
-
生成主要的幾個回撥介面,也就是上面建立的幾個 Callback,放入
mainCallbacks
陣列,可以回到上面的建構函式看看哦?0
:進行 AOP 代理的通用攔截器1
:執行目標方法的攔截器2
:空的 Callback 物件,例如finalize()
方法,不需要進行任何處理3
:目標物件排程器,用於獲取目標物件4
:配置管理器的排程器,會返回一個 AdvisedSupport 物件,因為代理物件會實現 Advised 介面,攔截到其裡面的方法時,需要呼叫當前 AdvisedSupport 的方法5
:處理equals(Object)
方法的攔截器6
:處理hashCode()
方法的攔截器 -
如果目標物件不需要每次都建立,且當前 AdvisedSupport 配置管理器被凍結了(預設不會),這一步做了一個效能優化,具體檢視上面的程式碼
在建立代理物件的時候,就可以先將目標物件的每個方法對應的方法呼叫器解析出來,該過程有點效能損耗,這樣在代理物件執行方法的時候效能有所提升;不過由於這裡會解析出許多方法呼叫器,會佔有一定的記憶體,以空間換時間
-
否則,不進行解析,取上面
mainCallbacks
-
返回這個目標物件對應的 Callback 陣列
獲取代理物件的 Callback 過程稍微有點複雜,因為代理物件處理不同的方法使用到的 Callback 也是不同的。不過你要知道的是上面第 2
步,第一個 Callback 就是 DynamicAdvisedInterceptor 物件,該物件用於進行 AOP 代理的通用攔截器,目標類的方法在這個攔截器中進行處理。這個物件和 JDK 動態代理建立的代理物件的實現差不多,所以都放入下一篇文章進行分析?
4.3 ProxyCallbackFilter
CglibAopProxy 的私有內部靜態類,用於篩選方法出對應的 Callback 物件
private static class ProxyCallbackFilter implements CallbackFilter {
private final AdvisedSupport advised;
private final Map<String, Integer> fixedInterceptorMap;
private final int fixedInterceptorOffset;
public ProxyCallbackFilter(
AdvisedSupport advised, Map<String, Integer> fixedInterceptorMap, int fixedInterceptorOffset) {
this.advised = advised;
this.fixedInterceptorMap = fixedInterceptorMap;
this.fixedInterceptorOffset = fixedInterceptorOffset;
}
/**
* 根據 Method 返回我們需要的 Callback 在陣列中的位置
*/
@Override
public int accept(Method method) {
// <1> 如果該方法是 `finalize()`
if (AopUtils.isFinalizeMethod(method)) {
// 返回一個空 Callback 物件,不對該方法做任何處理
return NO_OVERRIDE;
}
// <2> 如果該方法是 Advised 中的方法,也就是需要 AdvisedSupport 來執行
if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// 返回一個配置管理器的排程器,會返回一個 AdvisedSupport 物件去執行這個方法
return DISPATCH_ADVISED;
}
// We must always proxy equals, to direct calls to this.
// <3> 如果該方法是 `equals(Object)` 方法
if (AopUtils.isEqualsMethod(method)) {
// 返回處理 `equals(Object)` 方法的攔截器去執行該方法
return INVOKE_EQUALS;
}
// We must always calculate hashCode based on the proxy.
// <4> 如果該方法是 `hashCode()` 方法
if (AopUtils.isHashCodeMethod(method)) {
// 返回處理 `hashCode()` 方法的攔截器去執行該方法
return INVOKE_HASHCODE;
}
// <5> 獲取目標類 Class 物件
Class<?> targetClass = this.advised.getTargetClass();
// Proxy is not yet available, but that shouldn't matter.
// <6> 獲取能夠應用於該方法的所有攔截器,僅判斷是否存在 Advice
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
boolean haveAdvice = !chain.isEmpty();
boolean exposeProxy = this.advised.isExposeProxy();
boolean isStatic = this.advised.getTargetSource().isStatic();
boolean isFrozen = this.advised.isFrozen();
// <7> 如果有 Advice 或者配置沒有被凍結,通常情況都會進入這裡
if (haveAdvice || !isFrozen) {
// If exposing the proxy, then AOP_PROXY must be used.
// <7.1> 如果需要暴露這個代理物件,預設為 `false`
if (exposeProxy) {
// 返回處理 AOP 代理的通用攔截器去執行該方法
return AOP_PROXY;
}
String key = method.toString();
// Check to see if we have fixed interceptor to serve this method.
// Else use the AOP_PROXY.
// <7.2> 如果目標物件是單例的,且配置被凍結,且存在對應的方法呼叫器
if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
// We know that we are optimizing so we can use the FixedStaticChainInterceptors.
// 返回已經存在的方法呼叫器
int index = this.fixedInterceptorMap.get(key);
return (index + this.fixedInterceptorOffset);
}
// <7.3> 否則(通常會走到這一步)
else {
// 返回處理 AOP 代理的通用攔截器去執行該方法
return AOP_PROXY;
}
}
// <8> 否則
else {
// <8.1> 如果需要暴露代理物件,或者不是單例模式,預設這兩種情況都不滿足
if (exposeProxy || !isStatic) {
// 則返回執行目標方法的攔截器
return INVOKE_TARGET;
}
Class<?> returnType = method.getReturnType();
// <8.2> 如果該方法需要返回的就是目標類,暫時不清楚這種情況
if (targetClass != null && returnType.isAssignableFrom(targetClass)) {
// 那麼返回執行目標方法的攔截器,返回當前物件
return INVOKE_TARGET;
}
// <8.3> 否則,返回目標物件排程器,用於獲取目標物件,讓目標物件自己去執行這個方法
else {
return DISPATCH_TARGET;
}
}
}
}
我們直接看到實現的 accept(Method method)
方法的處理邏輯:
- 如果該方法是
finalize()
,返回一個空 Callback 物件,不對該方法做任何處理 - 如果該方法是 Advised 中的方法,也就是需要 AdvisedSupport 來執行,返回一個配置管理器的排程器,會返回一個 AdvisedSupport 物件去執行這個方法
- 如果該方法是
equals(Object)
方法,返回處理equals(Object)
方法的攔截器去執行該方法 - 如果該方法是
hashCode()
方法,返回處理hashCode()
方法的攔截器去執行該方法 - 獲取目標類 Class 物件
- 獲取能夠應用於該方法的所有攔截器,僅判斷是否存在 Advice
- 如果有 Advice 或者配置沒有被凍結,通常情況都會進入這裡
- 如果需要暴露這個代理物件,預設為
false
,返回處理 AOP 代理的通用攔截器去執行該方法 - 如果目標物件是單例的,且配置被凍結,且存在對應的方法呼叫器,返回已經存在的方法呼叫器
- 否則(通常會走到這一步),返回進行 AOP 代理的通用攔截器去執行該方法
- 如果需要暴露這個代理物件,預設為
- 否則
- 如果需要暴露代理物件,或者不是單例模式,預設這兩種情況都不滿足,返回執行目標方法的攔截器
- 如果該方法需要返回的就是目標類,暫時不清楚這種情況,那麼返回執行目標方法的攔截器,返回當前物件
- 否則,返回目標物件排程器,用於獲取目標物件,讓目標物件自己去執行這個方法
上面的處理過程基本上都根據 Method 進行判斷,然後返回一個 int
數值,這個值代表去取 Callback 陣列中的那個 Callback,回看上面講到的幾個方法你就全部理解了。
我們來看到上面的第 7.3
步,返回的是一個 0
,表示使用第一個 Callback 進行處理,對應的就是 DynamicAdvisedInterceptor 物件,這個物件和 JDK 動態代理建立的代理物件的實現差不多,所以都放入下一篇文章進行分析?
4.4 createProxyClassAndInstance 方法
createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks)
方法,建立代理物件,如下:
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
// 設定使用構造器期間不進行攔截
enhancer.setInterceptDuringConstruction(false);
// 設定 Callback 陣列
enhancer.setCallbacks(callbacks);
// 建立一個代理物件(目標類的子類)
return (this.constructorArgs != null && this.constructorArgTypes != null ?
// 使用指定的構造方法
enhancer.create(this.constructorArgTypes, this.constructorArgs) :
// 使用預設的構造方法
enhancer.create());
}
這個方法在 ObjenesisCglibAopProxy 子類中被重寫了,Spring 內部也是使用 ObjenesisCglibAopProxy 這個類,因為 CGLIB 建立的子類考慮到通過構造器例項化代理物件,可能會存在構造器不合適的情況,所以 Spring 採用 Objenesis 例項化物件,不通過構造器建立例項物件
4.4 ObjenesisCglibAopProxy
org.springframework.aop.framework.ObjenesisCglibAopProxy
,繼承 CglibAopProxy 類,重寫 createProxyClassAndInstance(..)
方法
class ObjenesisCglibAopProxy extends CglibAopProxy {
/**
* 建立一個 Objenesis 物件
* [**Objenesis**](http://objenesis.org/) 是一個小型 Java 庫,目的是為一些特殊的 Class 物件例項化一個物件
* 應用場景:
* 1. 序列化,遠端呼叫和持久化 - 物件需要例項化並儲存為到一個特殊的狀態,而沒有呼叫程式碼
* 2. 代理,AOP 庫和 Mock 物件 - 類可以被子類繼承而子類不用擔心父類的構造器
* 3. 容器框架 - 物件可以以非標準的方式被動態例項化
*/
private static final SpringObjenesis objenesis = new SpringObjenesis();
public ObjenesisCglibAopProxy(AdvisedSupport config) {
super(config);
}
@Override
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
// <1> 先建立代理物件的 Class 物件(目標類的子類)
Class<?> proxyClass = enhancer.createClass();
Object proxyInstance = null;
// <2> 是否使用 Objenesis 來例項化代理物件,預設會
// 可通過 在 `spring.properties` 檔案中新增 `spring.objenesis.ignore=false` 來禁止
if (objenesis.isWorthTrying()) {
try {
// <2.1> 通過 Objenesis 例項化代理物件(非標準方式,不使用構造方法進行例項化)
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
}
catch (Throwable ex) {
logger.debug("Unable to instantiate proxy using Objenesis, " +
"falling back to regular proxy construction", ex);
}
}
// <3> 如果藉助 Objenesis 例項化代理物件失敗
if (proxyInstance == null) {
// Regular instantiation via default constructor...
try {
// <3.1> 選擇構造器,指定了引數則使用對應的構造器,否則使用預設構造器
Constructor<?> ctor = (this.constructorArgs != null ?
proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
proxyClass.getDeclaredConstructor());
ReflectionUtils.makeAccessible(ctor);
// <3.2> 通過構造器例項化代理物件(反射)
proxyInstance = (this.constructorArgs != null ?
ctor.newInstance(this.constructorArgs) : ctor.newInstance());
}
catch (Throwable ex) {
throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
"and regular proxy instantiation via default constructor fails as well", ex);
}
}
// <4> 設定 Callback 陣列
((Factory) proxyInstance).setCallbacks(callbacks);
// <5> 返回代理物件
return proxyInstance;
}
}
首先我們來看到 SpringObjenesis 這個物件,它實現了 Objenesis 介面,Objenesis 是一個小型 Java 庫,目的是為一些特殊的 Class 物件例項化一個物件,應用場景:
- 序列化,遠端呼叫和持久化 - 物件需要例項化並儲存為到一個特殊的狀態,而沒有呼叫程式碼
- 代理,AOP 庫和 Mock 物件 - 類可以被子類繼承而子類不用擔心父類的構造器
- 容器框架 - 物件可以以非標準的方式被動態例項化
建立代理物件的過程如下:
- 先通過 Enhancer 建立代理物件的 Class 物件(目標類的子類)
- 是否使用 Objenesis 來例項化代理物件,預設會
- 通過 Objenesis 例項化代理物件(非標準方式,不使用構造方法進行例項化)
- 如果藉助 Objenesis 例項化代理物件失敗
- 選擇構造器,指定了引數則使用對應的構造器,否則使用預設構造器
- 通過構造器例項化代理物件(反射)
- 設定 Callback 陣列
- 返回代理物件
總結
在前面的《Spring AOP 自動代理(一)入口》文章中,分析了 Spring AOP 自動代理的入口是 AbstractAutoProxyCreator 物件,其中自動代理的過程主要分為下面兩步:
- 篩選出能夠應用於當前 Bean 的 Advisor
- 找到了合適 Advisor 則建立一個代理物件, JDK 動態代理或者 CGLIB 動態代理
上一篇《Spring AOP 自動代理(二)篩選合適的通知器》文章分析了上面第 1
步的處理過程。本文是接著前面兩篇文章分析上面第 2
個過程,Spring 是如何建立代理物件的,大致流程如下:
-
建立一個 ProxyFactory 代理工廠物件,設定需要建立的代理類的配置資訊,例如 Advisor 陣列和 TargetSource 目標類來源
-
藉助 DefaultAopProxyFactory 選擇 JdkDynamicAopProxy(JDK 動態代理)還是 ObjenesisCglibAopProxy(CGLIB 動態代理)
-
當
proxy-target-class
為false
時,優先使用 JDK 動態代理,如果目標類沒有實現可代理的介面,那麼還是使用 CGLIB 動態代理 -
如果為
true
,優先使用 CGLIB 動態代理,如果目標類本身是一個介面,那麼還是使用 JDK 動態代理
-
-
通過 JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy 建立一個代理物件
- JdkDynamicAopProxy 本身是一個 InvocationHandler 實現類,通過 JDK 的
Proxy.newProxyInstance(..)
建立代理物件 - ObjenesisCglibAopProxy 藉助 CGLIB 的 Enhancer 建立代理物件,會設定 Callback 陣列和 CallbackFilter 篩選器(選擇合適 Callback 處理對應的方法),整個過程相比於 JDK 動態代理更復雜點,主要的實現在 DynamicAdvisedInterceptor 方法攔截器中
- JdkDynamicAopProxy 本身是一個 InvocationHandler 實現類,通過 JDK 的
其中 CGLIB 例項化代理物件的過程使用到了 Objenesis,它是一個小型 Java 庫,目的是為一些特殊的 Class 物件例項化一個物件。因為 CGLIB 建立的子類考慮到通過構造器例項化代理物件,可能會存在構造器不合適的情況,所以 Spring 採用 Objenesis 例項化物件,不通過構造器建立例項物件。
好了,本篇文章就到這裡了,Spring AOP 中整個自動代理過程到這裡也就結束了。對於 JDK 動態代理和 CGLIB 動態代理建立的代理物件,它們的具體實現(或者說是方法攔截處理)在這裡並沒有體現出來,前者的實現在 JdkDynamicAopProxy 中,因為它實現了 InvocationHandler 介面,後者的實現則在 DynamicAdvisedInterceptor 方法攔截器中,這些內容將在下篇文章進行分析。