該系列文章是本人在學習 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 自動代理,以及代理對象的攔截處理到這里已經全部講述完了。其中肯定存在不少的問題,如有疑惑,可在留言區進行留言。