跳至主要內容

7.Spring AOP两种代理对象的拦截处理

Java突击队大约 27 分钟

7.Spring AOP两种代理对象的拦截处理

Spring 版本:5.1.14.RELEASE

在前面几篇文章依次介绍了 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 自动代理(二)筛选合适的通知器》open in new window这篇文章

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 自动代理(三)创建代理对象》open in new window文章中分析过,这里不再讲述,上一篇文章知道,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 自动代理,以及代理对象的拦截处理到这里已经全部讲述完了。其中肯定存在不少的问题,如有疑惑,可在留言区进行留言。

版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址:open in new window