死磕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 自动代理,以及代理对象的拦截处理到这里已经全部讲述完了。其中肯定存在不少的问题,如有疑惑,可在留言区进行留言。

posted @ 2021-04-22 14:55  月圆吖  阅读(2668)  评论(0编辑  收藏  举报