該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 Spring 原始碼分析 GitHub 地址 進行閱讀。
Spring 版本:5.1.14.RELEASE
在開始閱讀 Spring AOP 原始碼之前,需要對 Spring IoC 有一定的瞭解,可檢視我的 《死磕Spring之IoC篇 - 文章導讀》 這一系列文章
瞭解 AOP 相關術語,可先檢視 《Spring AOP 常見面試題) 》 這篇文章
該系列其他文章請檢視:《死磕 Spring 之 AOP 篇 - 文章導讀》
在前面幾篇文章依次介紹了 Spring AOP 自動代理的整個過程,入口在 AbstractAutoProxyCreator 這個類中,它實現了幾種 BeanPostProcessor介面,結合 Spring IoC,在 Bean 的載入過程中支援建立代理物件,通常在 Bean 的初始化後,也就是 Bean 處於一個“成熟態”的時候進行 AOP 代理。整個的處理過程比較複雜,需要找到當前 Spring 上下文所有的 Advisor,也就是 Advice 的容器介面,通常都是 PointcutAdvisor,還包含了一個 Pointcut 切點。接著就是從這些 Advisor 中篩選出能夠應用於這個 Bean 的 Advisor 出來,經過一些處理過程,最後通過 JdkDynamicAopProxy(JDK 動態代理)或者 ObjenesisCglibAopProxy(CGLIB 動態代理)建立一個代理物件。
本文將會分析 Spring AOP 建立的兩種代理物件的攔截處理是如何進行的。開始之前,我們得知道JDK 動態代理建立的代理物件,攔截處理在 InvocationHandler 實現類中;CGLIB 動態代理建立的代理物件,攔截處理在傳入的 Callback 回撥中,對於這兩種代理物件不是很熟悉的小夥伴可檢視我前面的文章?
JDK 動態代理
我們先來簡單回顧一下 Spring AOP 中 JDK 動態代理建立代理物件的過程,如下:
// JdkDynamicAopProxy.java
@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);
}
可以看到呼叫 Proxy#newProxyInstance(..)
方法的 InvocationHandler 入參就是 this
,也就是當前物件。我在前面的文章也講到過,JDK 動態代理建立的代理物件實現了入參中的介面,且繼承 Proxy,代理物件的攔截處理通過 Proxy 父類中 InvocationHandler 來完成,也就是入參中的 InvocationHandler 物件。那麼我們一起來看看 JdkDynamicAopProxy(實現了 InvocationHandler)是如何攔截處理的。
JdkDynamicAopProxy
org.springframework.aop.framework.JdkDynamicAopProxy
,JDK 動態代理類,實現了 InvocationHandler 介面,可建立代理物件
invoke 方法
invoke(Object proxy, Method method, Object[] args)
方法,JDK 動態代理建立的代理物件的攔截處理,如下:
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
/** 代理物件的配置資訊,例如儲存了 TargetSource 目標類來源、能夠應用於目標類的所有 Advisor */
private final AdvisedSupport advised;
/** 目標物件是否重寫了 equals 方法 */
private boolean equalsDefined;
/** 目標物件是否重寫了 hashCode 方法 */
private boolean hashCodeDefined;
/** 代理物件的攔截處理 */
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
// <1> 獲取目標類的 TargetSource 物件,用於獲取目標類
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// <2> 如果攔截到下面幾種情況的方法,則需要進行額外處理
// <2.1> 如果攔截到的方法是 `equals`,且目標類沒有重寫,則呼叫當前類重寫的 `equals` 方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// <2.2> 否則,如果攔截到的方法是 `hashCode`,且目標類沒有重寫,則呼叫當前類重寫的 `hashCode` 方法
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
// <2.3> 否則,如果攔截到的是 DecoratingProxy 中的方法,則通過 AopProxyUtils 工具類計算出目標類的 Class 物件
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// <2.4> 否則,如果攔截到的是 Advised 中的方法,則通過 AopUtils 工具類呼叫該方法(反射)
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// <3> 如果 `expose-proxy` 屬性為 `true`,則需要暴露當前代理物件
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// <3.1> 向 AopContext 中設定代理物件,並記錄 ThreadLocal 之前存放的代理物件
// 這樣一來,在 Advice 或者被攔截方法中可以通過 AopContext 獲取到這個代理物件
oldProxy = AopContext.setCurrentProxy(proxy);
// <3.2> 標記這個代理物件被暴露了
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
// <4> 獲取目標物件,以及它的 Class 物件
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
// <5> 獲取能夠應用於該方法的所有攔截器(有序)
// 不同的 AspectJ 根據 @Order 排序
// 同一個 AspectJ 中的 Advice 排序:AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
// <6> 如果攔截器鏈為空,則直接執行目標方法
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
// <6.1> 引數適配處理
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// <6.2> 執行目標方法(反射),並獲取返回結果
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
// <7> 否則,需要根據攔截器鏈去執行目標方法
else {
// We need to create a method invocation...
// <7.1> 建立一個方法呼叫器,並將前面第 `5` 步獲取到的攔截器鏈傳入其中
// 該物件就是 Joinpoint 物件
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// <7.2> 執行目標方法,以及所有的 MethodInterceptor 方法攔截器(Advice 通知器),並獲取返回結果
retVal = invocation.proceed();
}
// Massage return value if necessary.
// <8> 獲取目標方法返回值型別
Class<?> returnType = method.getReturnType();
// <9> 如果需要返回代理物件
if (retVal != null // 返回值不為空
&& retVal == target // 返回值就是當前目標物件
&& returnType != Object.class // 返回值型別不是 Object 型別
&& returnType.isInstance(proxy) // 返回值型別就是代理物件的型別
&& !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass()))
{
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
// 將當前代理物件作為返回結果
retVal = proxy;
}
// <10> 否則,如果返回值型別為原始型別(基本型別,不能為空)且方法的返回型別不是 Void,如果返回值為空則丟擲異常
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
// <11> 返回 `retVal` 返回結果
return retVal;
}
finally {
// <12> 如果目標物件不為空,且 TargetSource 不是靜態的(表示每次都得返回一個新的目標物件)
// 那麼需要釋放當前獲取到的目標物件,通常情況下我們的單例 Bean 對應的都是 SingletonTargetSource,不需要釋放
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
// <13> 如果暴露了當前代理物件,則需要將之前的代理物件重新設定到 ThreadLocal 中
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
}
整個的攔截處理過程如下:
- 獲取目標類的 TargetSource 物件,用於獲取目標類
- 如果攔截到下面幾種情況的方法,則需要進行額外處理
- 如果攔截到的方法是
equals
,且目標類沒有重寫,則呼叫當前類重寫的equals
方法 - 否則,如果攔截到的方法是
hashCode
,且目標類沒有重寫,則呼叫當前類重寫的hashCode
方法 - 否則,如果攔截到的是 DecoratingProxy 中的方法,則通過 AopProxyUtils 工具類計算出目標類的 Class 物件
- 否則,如果攔截到的是 Advised 中的方法,則通過 AopUtils 工具類呼叫該方法(反射)
- 如果攔截到的方法是
- 如果
expose-proxy
屬性為true
,則需要暴露當前代理物件- 向 AopContext 中設定代理物件,並記錄 ThreadLocal 之前存放的代理物件,這樣一來,在 Advice 或者被攔截方法中可以通過 AopContext 獲取到這個代理物件
- 標記這個代理物件被暴露了
- 獲取目標物件,以及它的 Class 物件
- 呼叫當前 AdvisedSupport 的
getInterceptorsAndDynamicInterceptionAdvice(..)
,獲取能夠應用於該方法的所有攔截器(有序)- 不同的 AspectJ 根據
@Order
排序 - 同一個 AspectJ 中的 Advice 排序:
AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
- 不同的 AspectJ 根據
- 如果攔截器鏈為空,則直接執行目標方法
- 引數適配處理
- 執行目標方法(反射),並獲取返回結果
- 否則,需要根據攔截器鏈去執行目標方法
- 建立一個 ReflectiveMethodInvocation 方法呼叫器,並將前面第
5
步獲取到的攔截器鏈傳入其中 - 執行方法呼叫器,會執行目標方法,以及所有的 MethodInterceptor 方法攔截器(Advice 通知器),並獲取返回結果
- 建立一個 ReflectiveMethodInvocation 方法呼叫器,並將前面第
- 獲取目標方法返回值型別
- 如果需要返回代理物件,將當前代理物件作為返回結果
- 否則,如果返回值型別為原始型別(基本型別,不能為空)且方法的返回型別不是 Void,如果返回值為空則丟擲異常
- 返回
retVal
返回結果 - 如果目標物件不為空,且 TargetSource 不是靜態的(表示每次都得返回一個新的目標物件),那麼需要釋放當前獲取到的目標物件,通常情況下我們的單例 Bean 對應的都是 SingletonTargetSource,不需要釋放
- 如果暴露了當前代理物件,則需要將之前的代理物件重新設定到 ThreadLocal 中
JDK 動態代理建立的代理物件的攔截處理過程整體邏輯上並不複雜,通過上面的描述大致上可以理解。上面過程複雜的是上面的第 5
步和第 7
步,一個是獲取該方法的攔截器們,一個是執行整個攔截器鏈,接下來我們依次分析
至於上面的第 5
步得到的方法攔截器們的順序為什麼是這個,可以檢視我前面的 《Spring AOP 自動代理(二)篩選合適的通知器》這篇文章
AdvisedSupport
org.springframework.aop.framework.AdvisedSupport
,代理物件的配置管理器
獲取能夠應用於方法的攔截器們
getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass)
方法,獲取能夠應用於該方法的所有攔截器,如下:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
// <1> 建立一個方法快取 Key
MethodCacheKey cacheKey = new MethodCacheKey(method);
// <2> 嘗試從快取中獲取
List<Object> cached = this.methodCache.get(cacheKey);
// 快取未命中,則進行下一步處理
if (cached == null) {
/*
* <3> 獲取能夠應用於該方法的所有攔截器(有序)
* 篩選出能夠應用於該方法的所有 Advisor,並獲取對應的 MethodInterceptor,也就是 Advice(如果不是方法攔截器則會包裝成對應的 MethodInterceptor)
* 因為 Advisor 是排好序的,所以返回的 MethodInterceptor 也是有序的
*
* 為什麼 `cached` 使用 `List<Object>` 儲存?
* 因為有些元素是 MethodInterceptor 和 MethodMatcher 的包裝物件,並不是 MethodInterceptor
*/
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
// <4> 將該方法對應的攔截器鏈路放入快取
this.methodCache.put(cacheKey, cached);
}
// <5> 返回能夠應用於該方法的所有攔截器(有序)
return cached;
}
該方法的處理過程如下:
- 建立一個方法快取 Key
- 嘗試從快取中獲取,快取未命中,則進行下一步處理
- 呼叫 DefaultAdvisorChainFactory 的
getInterceptorsAndDynamicInterceptionAdvice(..)
方法,獲取能夠應用於該方法的所有攔截器(有序)- 篩選出能夠應用於該方法的所有 Advisor,並獲取對應的 MethodInterceptor,也就是 Advice(如果不是方法攔截器則會包裝成對應的 MethodInterceptor)
- 因為 Advisor 是排好序的,所以返回的 MethodInterceptor 也是有序的
- 將該方法對應的攔截器鏈路放入快取
- 返回能夠應用於該方法的所有攔截器(有序)
這個方法的的處理過程只是嘗試去快取中獲取,快取未命中,則通過 DefaultAdvisorChainFactory 獲取能夠應用於該方法的所有攔截器
DefaultAdvisorChainFactory
org.springframework.aop.framework.DefaultAdvisorChainFactory
,獲取攔截器鏈路的預設實現
獲取能夠應用於方法的攔截器們
getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass)
方法,獲取能夠應用於該方法的所有攔截器,如下:
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// <1> 獲取 DefaultAdvisorAdapterRegistry 例項物件
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// <2> 獲取能夠應用到 `targetClass` 的 Advisor 們
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
// <3> 遍歷上一步獲取到的 Advisor 們
// 篩選出哪些 Advisor 需要處理當前被攔截的 `method`,並獲取對應的 MethodInterceptor(Advice,如果不是方法攔截器則會包裝成對應的 MethodInterceptor)
for (Advisor advisor : advisors) {
/*
* <3.1> 如果是 PointcutAdvisor 型別,則需要對目標物件的型別和被攔截的方法進行匹配
*/
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
/*
* <3.1.1> 判斷這個 PointcutAdvisor 是否匹配目標物件的型別,無法匹配則跳過
*/
if (config.isPreFiltered() // AdvisedSupport 是否已經過濾過目標物件的型別
|| pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) // 呼叫 Pointcut 的 ClassFilter 對目標物件的型別進行匹配
{
// <3.1.2> 獲取 Pointcut 的 MethodMatcher 方法匹配器對該方法進行匹配
// 參考 AspectJExpressionPointcut,底層藉助於 AspectJ 的處理
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
match = mm.matches(method, actualClass);
}
/*
* <3.1.3> 如果這個方法匹配成功,則進行下面的處理
*/
if (match) {
// <3.1.4> 從 Advisor 中獲取 Advice,幷包裝成 MethodInterceptor 攔截器物件(如果不是的話)
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
// <3.1.5> 若 MethodMatcher 的 `isRuntime()` 返回 `true`,則表明 MethodMatcher 要在執行時做一些檢測
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
// <3.1.5.1> 將上面獲取到的 MethodInterceptor 和 MethodMatcher 包裝成一個物件,並新增至 `interceptorList`
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
// <3.1.6> 否則,直接將 MethodInterceptor 們新增至 `interceptorList`
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
/*
* <3.2> 否則,如果是 IntroductionAdvisor 型別,則需要對目標物件的型別進行匹配
*/
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
/*
* <3.2.1> 判斷這個 IntroductionAdvisor 是否匹配目標物件的型別,無法匹配則跳過
*/
if (config.isPreFiltered() // AdvisedSupport 是否已經過濾過目標物件的型別
|| ia.getClassFilter().matches(actualClass)) // 呼叫 Pointcut 的 ClassFilter 對目標物件的型別進行匹配
{
// <3.2.2> 從 Advisor 中獲取 Advice,幷包裝成 MethodInterceptor 攔截器物件(如果不是的話)
Interceptor[] interceptors = registry.getInterceptors(advisor);
// <3.2.3> 直接將 MethodInterceptor 們新增至 `interceptorList`
interceptorList.addAll(Arrays.asList(interceptors));
}
}
/*
* <3.3> 否則,不需要對目標物件的型別和被攔截的方法進行匹配
*/
else {
// <3.3.1> 從 Advisor 中獲取 Advice,幷包裝成 MethodInterceptor 攔截器物件(如果不是的話)
Interceptor[] interceptors = registry.getInterceptors(advisor);
// <3.3.2> 直接將 MethodInterceptor 們新增至 `interceptorList`
interceptorList.addAll(Arrays.asList(interceptors));
}
}
// <4> 返回 `interceptorList` 所有的 MethodInterceptor 攔截器
// 因為 Advisor 是排好序的,所以這裡的 `interceptorList` 是有序的
return interceptorList;
}
該方法的處理過程如下:
- 獲取 DefaultAdvisorAdapterRegistry 例項物件
- 獲取能夠應用到
targetClass
的 Advisor 們 - 遍歷上一步獲取到的 Advisor 們,篩選出哪些 Advisor 需要處理當前被攔截的
method
,並獲取對應的 MethodInterceptor(Advice,如果不是方法攔截器則會包裝成對應的 MethodInterceptor)- 如果是
PointcutAdvisor
型別,則需要對目標物件的型別和被攔截的方法進行匹配- 判斷這個
PointcutAdvisor
是否匹配目標物件的型別(ClassFilter),無法匹配則跳過 - 獲取 Pointcut 的 MethodMatcher 方法匹配器對該方法進行匹配,參考 AspectJExpressionPointcut,底層藉助於 AspectJ 的處理
- 如果這個方法匹配成功,則進行下面的處理
- 通過 DefaultAdvisorAdapterRegistry 從 Advisor 中獲取 Advice,幷包裝成 MethodInterceptor 攔截器物件(如果不是的話)
- 若 MethodMatcher 的
isRuntime()
返回true
,則表明 MethodMatcher 要在執行時做一些檢測- 將上面獲取到的 MethodInterceptor 和 MethodMatcher 包裝成一個物件,並新增至
interceptorList
- 將上面獲取到的 MethodInterceptor 和 MethodMatcher 包裝成一個物件,並新增至
- 否則,直接將 MethodInterceptor 們新增至
interceptorList
- 判斷這個
- 否則,如果是
IntroductionAdvisor
型別,則需要對目標物件的型別進行匹配- 判斷這個
IntroductionAdvisor
是否匹配目標物件的型別(ClassFilter),無法匹配則跳過 - 通過 DefaultAdvisorAdapterRegistry 從 Advisor 中獲取 Advice,幷包裝成 MethodInterceptor 攔截器物件(如果不是的話)
- 直接將 MethodInterceptor 們新增至
interceptorList
- 判斷這個
- 否則,不需要對目標物件的型別和被攔截的方法進行匹配,直接通過
- 通過 DefaultAdvisorAdapterRegistry 從 Advisor 中獲取 Advice,幷包裝成 MethodInterceptor 攔截器物件(如果不是的話)
- 直接將 MethodInterceptor 們新增至
interceptorList
- 如果是
- 返回
interceptorList
所有的 MethodInterceptor 攔截器,因為 Advisor 是排好序的,所以這裡的interceptorList
是有序的
這裡做一個小結,整個處理過程並不複雜,當時建立代理物件的時候篩選出了能夠應用於當前 Bean 的所有 Advisor,現在要做的是先從這些 Advisor 中篩選出能夠應用於當前方法的 Advisor,然後通過 DefaultAdvisorAdapterRegistry 獲取 Advisor 對應的 MethodInterceptor 方法攔截器。對於 PointcutAdvisor
和 IntroductionAdvisor
處理稍微有點不同,因為前者多了一個 Pointcut,需要通過它的 MethodMatcher 對方法進行匹配,其他的差不多。
DefaultAdvisorAdapterRegistry
org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry
,預設的 Advisor 介面卡註冊中心,主要是對 Advisor 中的 Advice 進行匹配處理
wrap 方法
@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);
}
將 Advice 包裝成 Advisor 物件
getInterceptors 方法
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
// <1> 獲取 Advice 通知器
Advice advice = advisor.getAdvice();
/*
* <2> 若 Advice 是 MethodInterceptor 型別的,直接新增到 `interceptors`即可
* 例如 AspectJAfterThrowingAdvice、AspectJAfterAdvice、AspectJAroundAdvice
*/
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
/*
* <3> 通過 Advisor 介面卡將 Advice 封裝成對應的 MethodInterceptor 物件,並新增至 `interceptors`
* AspectJAfterReturningAdvice -> AfterReturningAdviceInterceptor
* AspectJMethodBeforeAdvice -> MethodBeforeAdviceInterceptor
*/
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
// <4> 沒有對應的 MethodInterceptor 則丟擲異常
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
// <5> 將 `interceptors` 轉換成陣列並返回
return interceptors.toArray(new MethodInterceptor[0]);
}
獲取 Advisor 中的 MethodInterceptor 方法攔截器:
- 獲取 Advice 通知器
- 若 Advice 是 MethodInterceptor 型別的,直接新增到
interceptors
即可- 例如
AspectJAfterThrowingAdvice、AspectJAfterAdvice、AspectJAroundAdvice
- 例如
- 通過 Advisor 介面卡將 Advice 封裝成對應的 MethodInterceptor 物件,並新增至
interceptors
AspectJAfterReturningAdvice -> AfterReturningAdviceInterceptor
AspectJMethodBeforeAdvice -> MethodBeforeAdviceInterceptor
- 沒有對應的 MethodInterceptor 則丟擲異常
- 將
interceptors
轉換成陣列並返回
可以看到,在 Spring AOP 的攔截處理中,使用的 Advice 都是 MethodInterceptor 方法攔截器
ReflectiveMethodInvocation
org.springframework.aop.framework.ReflectiveMethodInvocation
,代理物件目標方法的呼叫器,包含方法對應的 MethodInterceptor 攔截器鏈
建構函式
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
/** 代理物件 */
protected final Object proxy;
/** 目標物件 */
@Nullable
protected final Object target;
/** 目標方法 */
protected final Method method;
/** 方法入參 */
protected Object[] arguments;
/** 目標物件的 Class 物件 */
@Nullable
private final Class<?> targetClass;
/** 自定義屬性 */
@Nullable
private Map<String, Object> userAttributes;
/** 方法的攔截器鏈路 */
protected final List<?> interceptorsAndDynamicMethodMatchers;
/** 當前已經執行完的攔截器的位置索引,執行完則執行目標方法 */
private int currentInterceptorIndex = -1;
protected ReflectiveMethodInvocation(
Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
// 獲取目標方法,如果是橋接方法則會找到目標方法
this.method = BridgeMethodResolver.findBridgedMethod(method);
// 對該方法引數進行適配處理(如果有必要)
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
}
上面的屬性通過上面的註釋進行理解即可
invokeJoinpoint 方法
invokeJoinpoint()
方法,執行目標方法,如下:
@Nullable
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
} catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
} catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
基於反射執行目標方法
proceed 方法
proceed()
方法,方法呼叫器的執行,如下:
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// <1> 如果當前已經執行完的攔截器的位置索引就是最後一個,那麼即可執行目標方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 執行目標方法(底層反射機制)
return invokeJoinpoint();
}
// <2> 按順序獲取攔截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
/**
* <3> 如果是 InterceptorAndDynamicMethodMatcher 型別,表示 MethodMatcher 在真正的執行時需要做一些檢測
* 參考 {@link DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice }
*/
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// <3.1> 通過 MethodMatcher 對目標方法進行匹配
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
// 匹配通過,則執行這個攔截器,並傳遞當前物件
return dm.interceptor.invoke(this);
}
// <3.2> 否則,直接跳過這個攔截器
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// <4> 否則執行這個攔截器,並傳遞當前物件
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
方法呼叫器的執行過程如下:
- 如果
currentInterceptorIndex
(已經執行完的攔截器的位置索引)就是最後一個,那麼即可執行目標方法,呼叫invokeJoinpoint()
方法 - 按順序獲取攔截器,先將
currentInterceptorIndex
自增1
- 如果是 InterceptorAndDynamicMethodMatcher 型別,表示 MethodMatcher 在真正的執行時需要做一些檢測
- 通過 MethodMatcher 對目標方法進行匹配,匹配通過,則執行這個攔截器,並傳遞當前物件,呼叫
MethodInterceptor#invoke(this)
方法 - 否則,直接跳過這個攔截器,繼續呼叫當前
proceed()
方法
- 通過 MethodMatcher 對目標方法進行匹配,匹配通過,則執行這個攔截器,並傳遞當前物件,呼叫
- 否則執行這個攔截器,並傳遞當前物件,呼叫
MethodInterceptor#invoke(this)
方法
上面第 3
步,為什麼可能存在 InterceptorAndDynamicMethodMatcher 物件,返回前面的 DefaultAdvisorChainFactory 可以知曉答案
可以看到整個的方法呼叫過程是根據 MethodInterceptor 的順序一個一個往下執行的,執行完了則執行目標方法。
那麼你是否有疑問,為什麼執行完最後一個攔截器才執行目標方法,後置通知器不是需要等目標方法執行後才進行處理的嗎?其實你進入 Advice 實現的
MethodInterceptor#invoke(MethodInvocation)
方法就知道答案了,可檢視下面這張圖。
方法呼叫器的執行攔截器鏈圖
到這裡, JDK 動態代理建立的代理物件的攔截處理過程全部分析完了,做一個小的總結:
-
在攔截處理某個方法的時候,需要先通過 AdvisedSupport -> DefaultAdvisorChainFactory -> DefaultAdvisorAdapterRegistry 獲取到能夠應用於該方法的 Advisor 們,例項拿到的是它們對應的 MethodInterceptor 攔截器們,他們的順序如上面這張圖所示;
-
獲取到了 MethodInterceptor 方法攔截器後,建立一個 ReflectiveMethodInvocation 方法呼叫器,進行方法的攔截處理,依次呼叫每個 MethodInterceptor 的
invoke(..)
方法,在最後執行目標方法; -
在上面這張圖你可以看到不同 MethodInterceptor 的執行順序,不過實際 Advice 的執行邏輯順序是:
Around 前處理 > Before > Around 後處理 > After > AfterReturning|AfterThrowing
------------------------------------
CGLIB 動態代理
我們先來簡單回顧一下 Spring AOP 中 CGLIB 動態代理建立代理物件的物件:
// CglibAopProxy.java
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
// ... 下面只展示部分程式碼
// <5> 建立 CGLIB 的增強類,並進行接下來的配置
Enhancer enhancer = createEnhancer();
// <5.1> 設定被代理的類
enhancer.setSuperclass(proxySuperClass);
// <5.2> 設定需要代理的介面(可能沒有,不過都會加上 Spring 內部的幾個介面,例如 SpringProxy)
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
// <5.3> 設定命名策略,預設生成的代理物件的名稱中包含 '$$' 和 'BySpringCGLIB'
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// <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();
}
// <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);
}
可以看到通過 Enhancer 建立代理物件,也就是目標類的子類,設定了一個 Callback 陣列和 CallbackFilter 過濾器,CallbackFilter 用於獲取目標方法對應的 Callback。這些內容都在上一篇《Spring AOP 自動代理(三)建立代理物件》文章中分析過,這裡不再講述,上一篇文章知道,CGLIB 進行 AOP 代理的通用攔截器是 DynamicAdvisedInterceptor 物件,那麼接下來我們來看看這個攔截是怎麼處理的。
DynamicAdvisedInterceptor
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor
,CglibAopProxy 的私有靜態內部類
intercept 方法
intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
方法,CGLIB 動態代理建立代理物件的通用攔截處理方法,如下:
private final AdvisedSupport advised;
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
// <1> 獲取目標類的 TargetSource 物件,用於獲取目標類
TargetSource targetSource = this.advised.getTargetSource();
try {
// <2> 如果 `expose-proxy` 屬性為 `true`,則需要暴露當前代理物件
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// <2.1> 向 AopContext 中設定代理物件,並記錄 ThreadLocal 之前存放的代理物件
// 這樣一來,在 Advice 或者被攔截方法中可以通過 AopContext 獲取到這個代理物件
oldProxy = AopContext.setCurrentProxy(proxy);
// <2.2> 標記這個代理物件被暴露了
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
// <3> 獲取目標物件,以及它的 Class 物件
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// <4> 獲取能夠應用於該方法的所有攔截器(有序)
// 不同的 AspectJ 根據 @Order 排序
// 同一個 AspectJ 中的 Advice 排序:AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// <5> 如果攔截器鏈為空,則直接執行目標方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// <5.1> 引數適配處理
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// <5.2> 執行目標方法(反射),並獲取返回結果
retVal = methodProxy.invoke(target, argsToUse);
}
// <6> 否則,需要根據攔截器鏈去執行目標方法
else {
// <6.1> 建立一個方法呼叫器,並將前面獲取到的攔截器鏈傳入其中,該物件就是 Joinpoint 物件
// <6.2> 執行目標方法,以及所有的 MethodInterceptor 方法攔截器(Advice 通知器),並獲取返回結果
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
// <7> 對最終的返回結果進一步處理(返回結果是否需要為代理物件,返回結果是否不能為空)
retVal = processReturnType(proxy, target, method, retVal);
// <8> 返回 `retVal` 返回結果
return retVal;
}
finally {
// <9> 如果目標物件不為空,且 TargetSource 不是靜態的(表示每次都得返回一個新的目標物件)
// 那麼需要釋放當前獲取到的目標物件,通常情況下我們的單例 Bean 對應的都是 SingletonTargetSource,不需要釋放
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
// <10> 如果暴露了當前代理物件,則需要將之前的代理物件重新設定到 ThreadLocal 中
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
整個的攔截處理過程:
- 獲取目標類的 TargetSource 物件,用於獲取目標類
- 如果
expose-proxy
屬性為true
,則需要暴露當前代理物件- 向 AopContext 中設定代理物件,並記錄 ThreadLocal 之前存放的代理物件
- 標記這個代理物件被暴露了
- 獲取目標物件,以及它的 Class 物件
- 呼叫當前 AdvisedSupport 的
getInterceptorsAndDynamicInterceptionAdvice(..)
,獲取能夠應用於該方法的所有攔截器(有序)- 不同的 AspectJ 根據
@Order
排序 - 同一個 AspectJ 中的 Advice 排序:
AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
- 不同的 AspectJ 根據
- 如果攔截器鏈為空,則直接執行目標方法
- 引數適配處理
- 執行目標方法(反射),並獲取返回結果
- 否則,需要根據攔截器鏈去執行目標方法
- 建立一個 CglibMethodInvocation 方法呼叫器,並將前面第
5
步獲取到的攔截器鏈傳入其中 - 執行方法呼叫器,會執行目標方法,以及所有的 MethodInterceptor 方法攔截器(Advice 通知器),並獲取返回結果
- 建立一個 CglibMethodInvocation 方法呼叫器,並將前面第
- 對最終的返回結果進一步處理(返回結果是否需要為代理物件,返回結果是否不能為空)
- 返回
retVal
返回結果 - 如果目標物件不為空,且 TargetSource 不是靜態的(表示每次都得返回一個新的目標物件),那麼需要釋放當前獲取到的目標物件,通常情況下我們的單例 Bean 對應的都是 SingletonTargetSource,不需要釋放
- 如果暴露了當前代理物件,則需要將之前的代理物件重新設定到 ThreadLocal 中
CGLIB 動態代理建立代理物件的通用攔截處理過程,和前面講到的 JDK 動態代理建立的代理物件差不多,這裡不再講述。這裡不同的是建立的方法呼叫器是 CglibMethodInvocation 物件,我們一起來看看這個物件
CglibMethodInvocation
org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation
,CglibAopProxy 的私有靜態內部類
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
@Nullable
private final MethodProxy methodProxy;
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
Object[] arguments, @Nullable Class<?> targetClass,
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
// Only use method proxy for public methods not derived from java.lang.Object
// 設定代理方法物件
this.methodProxy = (Modifier.isPublic(method.getModifiers()) // 方法被 public 修飾
&& method.getDeclaringClass() != Object.class // 不是 Object 中的方法
&& !AopUtils.isEqualsMethod(method) // 不是 `equals` 方法
&& !AopUtils.isHashCodeMethod(method) // 不是 `hashCode` 方法
&& !AopUtils.isToStringMethod(method) // 不是 `toString` 方法
? methodProxy : null);
}
@Override
protected Object invokeJoinpoint() throws Throwable {
if (this.methodProxy != null) {
// 執行代理方法物件(反射)
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
// 執行目標方法物件(反射)
return super.invokeJoinpoint();
}
}
}
可以看到它繼承了 ReflectiveMethodInvocation 這個類,重寫了 invokeJoinpoint()
執行目標方法的方法,區別在於 CGLIB 使用 MethodProxy 執行目標方法
總結
Spring AOP 有 JDK 動態代理 和 CGLIB 動態代理 兩種建立代理物件的方式,前者通過 JdkDynamicAopProxy
建立代理物件,對應的 InvocationHandler
就是這個 JdkDynamicAopProxy
物件;後者通過 CglibAopPeoxy
建立代理物件,會設定了一個 Callback 陣列和 CallbackFilter 過濾器,CallbackFilter 用於獲取目標方法對應的 Callback,其中進行 AOP 代理的通用攔截器是 DynamicAdvisedInterceptor 方法攔截器。
JdkDynamicAopProxy
:先獲取到能應用於方法的所有 MethodInterceptor(也就是 Advice),然後通過 ReflectiveMethodInvocation 方法呼叫器進行處理DynamicAdvisedInterceptor
:和上者處理邏輯差不多,區別是通過 CglibMethodInvocation 方法呼叫器進行處理,它重寫了 ReflectiveMethodInvocation 執行目標方法的方法
至於 ReflectiveMethodInvocation 方法呼叫器的執行邏輯可以參考上面的講解
到這裡,關於 Spring AOP 自動代理,以及代理物件的攔截處理到這裡已經全部講述完了。其中肯定存在不少的問題,如有疑惑,可在留言區進行留言。