死磕Spring之AOP篇 - Spring AOP兩種代理對象的攔截處理


該系列文章是本人在學習 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);
			}
		}
	}
}

整個的攔截處理過程如下:

  1. 獲取目標類的 TargetSource 對象,用於獲取目標類
  2. 如果攔截到下面幾種情況的方法,則需要進行額外處理
    1. 如果攔截到的方法是 equals,且目標類沒有重寫,則調用當前類重寫的 equals 方法
    2. 否則,如果攔截到的方法是 hashCode,且目標類沒有重寫,則調用當前類重寫的 hashCode 方法
    3. 否則,如果攔截到的是 DecoratingProxy 中的方法,則通過 AopProxyUtils 工具類計算出目標類的 Class 對象
    4. 否則,如果攔截到的是 Advised 中的方法,則通過 AopUtils 工具類調用該方法(反射)
  3. 如果 expose-proxy 屬性為 true,則需要暴露當前代理對象
    1. 向 AopContext 中設置代理對象,並記錄 ThreadLocal 之前存放的代理對象,這樣一來,在 Advice 或者被攔截方法中可以通過 AopContext 獲取到這個代理對象
    2. 標記這個代理對象被暴露了
  4. 獲取目標對象,以及它的 Class 對象
  5. 調用當前 AdvisedSupport 的 getInterceptorsAndDynamicInterceptionAdvice(..),獲取能夠應用於該方法的所有攔截器(有序)
    • 不同的 AspectJ 根據 @Order 排序
    • 同一個 AspectJ 中的 Advice 排序:AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
  6. 如果攔截器鏈為空,則直接執行目標方法
    1. 參數適配處理
    2. 執行目標方法(反射),並獲取返回結果
  7. 否則,需要根據攔截器鏈去執行目標方法
    1. 創建一個 ReflectiveMethodInvocation 方法調用器,並將前面第 5 步獲取到的攔截器鏈傳入其中
    2. 執行方法調用器,會執行目標方法,以及所有的 MethodInterceptor 方法攔截器(Advice 通知器),並獲取返回結果
  8. 獲取目標方法返回值類型
  9. 如果需要返回代理對象,將當前代理對象作為返回結果
  10. 否則,如果返回值類型為原始類型(基本類型,不能為空)且方法的返回類型不是 Void,如果返回值為空則拋出異常
  11. 返回 retVal 返回結果
  12. 如果目標對象不為空,且 TargetSource 不是靜態的(表示每次都得返回一個新的目標對象),那么需要釋放當前獲取到的目標對象,通常情況下我們的單例 Bean 對應的都是 SingletonTargetSource,不需要釋放
  13. 如果暴露了當前代理對象,則需要將之前的代理對象重新設置到 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;
}

該方法的處理過程如下:

  1. 創建一個方法緩存 Key
  2. 嘗試從緩存中獲取,緩存未命中,則進行下一步處理
  3. 調用 DefaultAdvisorChainFactory 的 getInterceptorsAndDynamicInterceptionAdvice(..) 方法,獲取能夠應用於該方法的所有攔截器(有序)
    • 篩選出能夠應用於該方法的所有 Advisor,並獲取對應的 MethodInterceptor,也就是 Advice(如果不是方法攔截器則會包裝成對應的 MethodInterceptor)
    • 因為 Advisor 是排好序的,所以返回的 MethodInterceptor 也是有序的
  4. 將該方法對應的攔截器鏈路放入緩存
  5. 返回能夠應用於該方法的所有攔截器(有序)

這個方法的的處理過程只是嘗試去緩存中獲取,緩存未命中,則通過 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;
}

該方法的處理過程如下:

  1. 獲取 DefaultAdvisorAdapterRegistry 實例對象
  2. 獲取能夠應用到 targetClass 的 Advisor 們
  3. 遍歷上一步獲取到的 Advisor 們,篩選出哪些 Advisor 需要處理當前被攔截的 method,並獲取對應的 MethodInterceptor(Advice,如果不是方法攔截器則會包裝成對應的 MethodInterceptor)
    1. 如果是 PointcutAdvisor 類型,則需要對目標對象的類型和被攔截的方法進行匹配
      1. 判斷這個 PointcutAdvisor 是否匹配目標對象的類型(ClassFilter),無法匹配則跳過
      2. 獲取 Pointcut 的 MethodMatcher 方法匹配器對該方法進行匹配,參考 AspectJExpressionPointcut,底層借助於 AspectJ 的處理
      3. 如果這個方法匹配成功,則進行下面的處理
      4. 通過 DefaultAdvisorAdapterRegistry 從 Advisor 中獲取 Advice,並包裝成 MethodInterceptor 攔截器對象(如果不是的話)
      5. 若 MethodMatcher 的 isRuntime() 返回 true,則表明 MethodMatcher 要在運行時做一些檢測
        1. 將上面獲取到的 MethodInterceptor 和 MethodMatcher 包裝成一個對象,並添加至 interceptorList
      6. 否則,直接將 MethodInterceptor 們添加至 interceptorList
    2. 否則,如果是 IntroductionAdvisor 類型,則需要對目標對象的類型進行匹配
      1. 判斷這個 IntroductionAdvisor 是否匹配目標對象的類型(ClassFilter),無法匹配則跳過
      2. 通過 DefaultAdvisorAdapterRegistry 從 Advisor 中獲取 Advice,並包裝成 MethodInterceptor 攔截器對象(如果不是的話)
      3. 直接將 MethodInterceptor 們添加至 interceptorList
    3. 否則,不需要對目標對象的類型和被攔截的方法進行匹配,直接通過
      1. 通過 DefaultAdvisorAdapterRegistry 從 Advisor 中獲取 Advice,並包裝成 MethodInterceptor 攔截器對象(如果不是的話)
      2. 直接將 MethodInterceptor 們添加至 interceptorList
  4. 返回 interceptorList 所有的 MethodInterceptor 攔截器,因為 Advisor 是排好序的,所以這里的 interceptorList 是有序的

這里做一個小結,整個處理過程並不復雜,當時創建代理對象的時候篩選出了能夠應用於當前 Bean 的所有 Advisor,現在要做的是先從這些 Advisor 中篩選出能夠應用於當前方法的 Advisor,然后通過 DefaultAdvisorAdapterRegistry 獲取 Advisor 對應的 MethodInterceptor 方法攔截器。對於 PointcutAdvisorIntroductionAdvisor 處理稍微有點不同,因為前者多了一個 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 方法攔截器:

  1. 獲取 Advice 通知器
  2. 若 Advice 是 MethodInterceptor 類型的,直接添加到 interceptors即可
    • 例如 AspectJAfterThrowingAdvice、AspectJAfterAdvice、AspectJAroundAdvice
  3. 通過 Advisor 適配器將 Advice 封裝成對應的 MethodInterceptor 對象,並添加至 interceptors
    • AspectJAfterReturningAdvice -> AfterReturningAdviceInterceptor
    • AspectJMethodBeforeAdvice -> MethodBeforeAdviceInterceptor
  4. 沒有對應的 MethodInterceptor 則拋出異常
  5. 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);
    }
}

方法調用器的執行過程如下:

  1. 如果 currentInterceptorIndex(已經執行完的攔截器的位置索引)就是最后一個,那么即可執行目標方法,調用 invokeJoinpoint() 方法
  2. 按順序獲取攔截器,先將 currentInterceptorIndex 自增 1
  3. 如果是 InterceptorAndDynamicMethodMatcher 類型,表示 MethodMatcher 在真正的執行時需要做一些檢測
    1. 通過 MethodMatcher 對目標方法進行匹配,匹配通過,則執行這個攔截器,並傳遞當前對象,調用 MethodInterceptor#invoke(this) 方法
    2. 否則,直接跳過這個攔截器,繼續調用當前 proceed() 方法
  4. 否則執行這個攔截器,並傳遞當前對象,調用 MethodInterceptor#invoke(this) 方法

上面第 3 步,為什么可能存在 InterceptorAndDynamicMethodMatcher 對象,返回前面的 DefaultAdvisorChainFactory 可以知曉答案

可以看到整個的方法調用過程是根據 MethodInterceptor 的順序一個一個往下執行的,執行完了則執行目標方法。

那么你是否有疑問,為什么執行完最后一個攔截器才執行目標方法,后置通知器不是需要等目標方法執行后才進行處理的嗎?其實你進入 Advice 實現的 MethodInterceptor#invoke(MethodInvocation) 方法就知道答案了,可查看下面這張圖。

方法調用器的執行攔截器鏈圖

到這里, JDK 動態代理創建的代理對象的攔截處理過程全部分析完了,做一個小的總結:

  1. 在攔截處理某個方法的時候,需要先通過 AdvisedSupport -> DefaultAdvisorChainFactory -> DefaultAdvisorAdapterRegistry 獲取到能夠應用於該方法的 Advisor 們,實例拿到的是它們對應的 MethodInterceptor 攔截器們,他們的順序如上面這張圖所示;

  2. 獲取到了 MethodInterceptor 方法攔截器后,創建一個 ReflectiveMethodInvocation 方法調用器,進行方法的攔截處理,依次調用每個 MethodInterceptor 的 invoke(..) 方法,在最后執行目標方法;

  3. 在上面這張圖你可以看到不同 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);
        }
    }
}

整個的攔截處理過程:

  1. 獲取目標類的 TargetSource 對象,用於獲取目標類
  2. 如果 expose-proxy 屬性為 true,則需要暴露當前代理對象
    1. 向 AopContext 中設置代理對象,並記錄 ThreadLocal 之前存放的代理對象
    2. 標記這個代理對象被暴露了
  3. 獲取目標對象,以及它的 Class 對象
  4. 調用當前 AdvisedSupport 的 getInterceptorsAndDynamicInterceptionAdvice(..),獲取能夠應用於該方法的所有攔截器(有序)
    • 不同的 AspectJ 根據 @Order 排序
    • 同一個 AspectJ 中的 Advice 排序:AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
  5. 如果攔截器鏈為空,則直接執行目標方法
    1. 參數適配處理
    2. 執行目標方法(反射),並獲取返回結果
  6. 否則,需要根據攔截器鏈去執行目標方法
    1. 創建一個 CglibMethodInvocation 方法調用器,並將前面第 5 步獲取到的攔截器鏈傳入其中
    2. 執行方法調用器,會執行目標方法,以及所有的 MethodInterceptor 方法攔截器(Advice 通知器),並獲取返回結果
  7. 對最終的返回結果進一步處理(返回結果是否需要為代理對象,返回結果是否不能為空)
  8. 返回 retVal 返回結果
  9. 如果目標對象不為空,且 TargetSource 不是靜態的(表示每次都得返回一個新的目標對象),那么需要釋放當前獲取到的目標對象,通常情況下我們的單例 Bean 對應的都是 SingletonTargetSource,不需要釋放
  10. 如果暴露了當前代理對象,則需要將之前的代理對象重新設置到 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 自動代理,以及代理對象的攔截處理到這里已經全部講述完了。其中肯定存在不少的問題,如有疑惑,可在留言區進行留言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM