死磕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);
}
}
}
}
整个的拦截处理过程如下:
- 获取目标类的 TargetSource 对象,用于获取目标类
- 如果拦截到下面几种情况的方法,则需要进行额外处理
- 如果拦截到的方法是
equals
,且目标类没有重写,则调用当前类重写的equals
方法 - 否则,如果拦截到的方法是
hashCode
,且目标类没有重写,则调用当前类重写的hashCode
方法 - 否则,如果拦截到的是 DecoratingProxy 中的方法,则通过 AopProxyUtils 工具类计算出目标类的 Class 对象
- 否则,如果拦截到的是 Advised 中的方法,则通过 AopUtils 工具类调用该方法(反射)
- 如果拦截到的方法是
- 如果
expose-proxy
属性为true
,则需要暴露当前代理对象- 向 AopContext 中设置代理对象,并记录 ThreadLocal 之前存放的代理对象,这样一来,在 Advice 或者被拦截方法中可以通过 AopContext 获取到这个代理对象
- 标记这个代理对象被暴露了
- 获取目标对象,以及它的 Class 对象
- 调用当前 AdvisedSupport 的
getInterceptorsAndDynamicInterceptionAdvice(..)
,获取能够应用于该方法的所有拦截器(有序)- 不同的 AspectJ 根据
@Order
排序 - 同一个 AspectJ 中的 Advice 排序:
AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
- 不同的 AspectJ 根据
- 如果拦截器链为空,则直接执行目标方法
- 参数适配处理
- 执行目标方法(反射),并获取返回结果
- 否则,需要根据拦截器链去执行目标方法
- 创建一个 ReflectiveMethodInvocation 方法调用器,并将前面第
5
步获取到的拦截器链传入其中 - 执行方法调用器,会执行目标方法,以及所有的 MethodInterceptor 方法拦截器(Advice 通知器),并获取返回结果
- 创建一个 ReflectiveMethodInvocation 方法调用器,并将前面第
- 获取目标方法返回值类型
- 如果需要返回代理对象,将当前代理对象作为返回结果
- 否则,如果返回值类型为原始类型(基本类型,不能为空)且方法的返回类型不是 Void,如果返回值为空则抛出异常
- 返回
retVal
返回结果 - 如果目标对象不为空,且 TargetSource 不是静态的(表示每次都得返回一个新的目标对象),那么需要释放当前获取到的目标对象,通常情况下我们的单例 Bean 对应的都是 SingletonTargetSource,不需要释放
- 如果暴露了当前代理对象,则需要将之前的代理对象重新设置到 ThreadLocal 中
JDK 动态代理创建的代理对象的拦截处理过程整体逻辑上并不复杂,通过上面的描述大致上可以理解。上面过程复杂的是上面的第 5
步和第 7
步,一个是获取该方法的拦截器们,一个是执行整个拦截器链,接下来我们依次分析
至于上面的第 5
步得到的方法拦截器们的顺序为什么是这个,可以查看我前面的 《Spring AOP 自动代理(二)筛选合适的通知器》这篇文章
AdvisedSupport
org.springframework.aop.framework.AdvisedSupport
,代理对象的配置管理器
获取能够应用于方法的拦截器们
getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass)
方法,获取能够应用于该方法的所有拦截器,如下:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
// <1> 创建一个方法缓存 Key
MethodCacheKey cacheKey = new MethodCacheKey(method);
// <2> 尝试从缓存中获取
List<Object> cached = this.methodCache.get(cacheKey);
// 缓存未命中,则进行下一步处理
if (cached == null) {
/*
* <3> 获取能够应用于该方法的所有拦截器(有序)
* 筛选出能够应用于该方法的所有 Advisor,并获取对应的 MethodInterceptor,也就是 Advice(如果不是方法拦截器则会包装成对应的 MethodInterceptor)
* 因为 Advisor 是排好序的,所以返回的 MethodInterceptor 也是有序的
*
* 为什么 `cached` 使用 `List<Object>` 存储?
* 因为有些元素是 MethodInterceptor 和 MethodMatcher 的包装对象,并不是 MethodInterceptor
*/
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
// <4> 将该方法对应的拦截器链路放入缓存
this.methodCache.put(cacheKey, cached);
}
// <5> 返回能够应用于该方法的所有拦截器(有序)
return cached;
}
该方法的处理过程如下:
- 创建一个方法缓存 Key
- 尝试从缓存中获取,缓存未命中,则进行下一步处理
- 调用 DefaultAdvisorChainFactory 的
getInterceptorsAndDynamicInterceptionAdvice(..)
方法,获取能够应用于该方法的所有拦截器(有序)- 筛选出能够应用于该方法的所有 Advisor,并获取对应的 MethodInterceptor,也就是 Advice(如果不是方法拦截器则会包装成对应的 MethodInterceptor)
- 因为 Advisor 是排好序的,所以返回的 MethodInterceptor 也是有序的
- 将该方法对应的拦截器链路放入缓存
- 返回能够应用于该方法的所有拦截器(有序)
这个方法的的处理过程只是尝试去缓存中获取,缓存未命中,则通过 DefaultAdvisorChainFactory 获取能够应用于该方法的所有拦截器
DefaultAdvisorChainFactory
org.springframework.aop.framework.DefaultAdvisorChainFactory
,获取拦截器链路的默认实现
获取能够应用于方法的拦截器们
getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass)
方法,获取能够应用于该方法的所有拦截器,如下:
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// <1> 获取 DefaultAdvisorAdapterRegistry 实例对象
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// <2> 获取能够应用到 `targetClass` 的 Advisor 们
Advisor[] advisors = config.getAdvisors();
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
// <3> 遍历上一步获取到的 Advisor 们
// 筛选出哪些 Advisor 需要处理当前被拦截的 `method`,并获取对应的 MethodInterceptor(Advice,如果不是方法拦截器则会包装成对应的 MethodInterceptor)
for (Advisor advisor : advisors) {
/*
* <3.1> 如果是 PointcutAdvisor 类型,则需要对目标对象的类型和被拦截的方法进行匹配
*/
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
/*
* <3.1.1> 判断这个 PointcutAdvisor 是否匹配目标对象的类型,无法匹配则跳过
*/
if (config.isPreFiltered() // AdvisedSupport 是否已经过滤过目标对象的类型
|| pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) // 调用 Pointcut 的 ClassFilter 对目标对象的类型进行匹配
{
// <3.1.2> 获取 Pointcut 的 MethodMatcher 方法匹配器对该方法进行匹配
// 参考 AspectJExpressionPointcut,底层借助于 AspectJ 的处理
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
match = mm.matches(method, actualClass);
}
/*
* <3.1.3> 如果这个方法匹配成功,则进行下面的处理
*/
if (match) {
// <3.1.4> 从 Advisor 中获取 Advice,并包装成 MethodInterceptor 拦截器对象(如果不是的话)
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
// <3.1.5> 若 MethodMatcher 的 `isRuntime()` 返回 `true`,则表明 MethodMatcher 要在运行时做一些检测
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
// <3.1.5.1> 将上面获取到的 MethodInterceptor 和 MethodMatcher 包装成一个对象,并添加至 `interceptorList`
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
// <3.1.6> 否则,直接将 MethodInterceptor 们添加至 `interceptorList`
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
/*
* <3.2> 否则,如果是 IntroductionAdvisor 类型,则需要对目标对象的类型进行匹配
*/
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
/*
* <3.2.1> 判断这个 IntroductionAdvisor 是否匹配目标对象的类型,无法匹配则跳过
*/
if (config.isPreFiltered() // AdvisedSupport 是否已经过滤过目标对象的类型
|| ia.getClassFilter().matches(actualClass)) // 调用 Pointcut 的 ClassFilter 对目标对象的类型进行匹配
{
// <3.2.2> 从 Advisor 中获取 Advice,并包装成 MethodInterceptor 拦截器对象(如果不是的话)
Interceptor[] interceptors = registry.getInterceptors(advisor);
// <3.2.3> 直接将 MethodInterceptor 们添加至 `interceptorList`
interceptorList.addAll(Arrays.asList(interceptors));
}
}
/*
* <3.3> 否则,不需要对目标对象的类型和被拦截的方法进行匹配
*/
else {
// <3.3.1> 从 Advisor 中获取 Advice,并包装成 MethodInterceptor 拦截器对象(如果不是的话)
Interceptor[] interceptors = registry.getInterceptors(advisor);
// <3.3.2> 直接将 MethodInterceptor 们添加至 `interceptorList`
interceptorList.addAll(Arrays.asList(interceptors));
}
}
// <4> 返回 `interceptorList` 所有的 MethodInterceptor 拦截器
// 因为 Advisor 是排好序的,所以这里的 `interceptorList` 是有序的
return interceptorList;
}
该方法的处理过程如下:
- 获取 DefaultAdvisorAdapterRegistry 实例对象
- 获取能够应用到
targetClass
的 Advisor 们 - 遍历上一步获取到的 Advisor 们,筛选出哪些 Advisor 需要处理当前被拦截的
method
,并获取对应的 MethodInterceptor(Advice,如果不是方法拦截器则会包装成对应的 MethodInterceptor)- 如果是
PointcutAdvisor
类型,则需要对目标对象的类型和被拦截的方法进行匹配- 判断这个
PointcutAdvisor
是否匹配目标对象的类型(ClassFilter),无法匹配则跳过 - 获取 Pointcut 的 MethodMatcher 方法匹配器对该方法进行匹配,参考 AspectJExpressionPointcut,底层借助于 AspectJ 的处理
- 如果这个方法匹配成功,则进行下面的处理
- 通过 DefaultAdvisorAdapterRegistry 从 Advisor 中获取 Advice,并包装成 MethodInterceptor 拦截器对象(如果不是的话)
- 若 MethodMatcher 的
isRuntime()
返回true
,则表明 MethodMatcher 要在运行时做一些检测- 将上面获取到的 MethodInterceptor 和 MethodMatcher 包装成一个对象,并添加至
interceptorList
- 将上面获取到的 MethodInterceptor 和 MethodMatcher 包装成一个对象,并添加至
- 否则,直接将 MethodInterceptor 们添加至
interceptorList
- 判断这个
- 否则,如果是
IntroductionAdvisor
类型,则需要对目标对象的类型进行匹配- 判断这个
IntroductionAdvisor
是否匹配目标对象的类型(ClassFilter),无法匹配则跳过 - 通过 DefaultAdvisorAdapterRegistry 从 Advisor 中获取 Advice,并包装成 MethodInterceptor 拦截器对象(如果不是的话)
- 直接将 MethodInterceptor 们添加至
interceptorList
- 判断这个
- 否则,不需要对目标对象的类型和被拦截的方法进行匹配,直接通过
- 通过 DefaultAdvisorAdapterRegistry 从 Advisor 中获取 Advice,并包装成 MethodInterceptor 拦截器对象(如果不是的话)
- 直接将 MethodInterceptor 们添加至
interceptorList
- 如果是
- 返回
interceptorList
所有的 MethodInterceptor 拦截器,因为 Advisor 是排好序的,所以这里的interceptorList
是有序的
这里做一个小结,整个处理过程并不复杂,当时创建代理对象的时候筛选出了能够应用于当前 Bean 的所有 Advisor,现在要做的是先从这些 Advisor 中筛选出能够应用于当前方法的 Advisor,然后通过 DefaultAdvisorAdapterRegistry 获取 Advisor 对应的 MethodInterceptor 方法拦截器。对于 PointcutAdvisor
和 IntroductionAdvisor
处理稍微有点不同,因为前者多了一个 Pointcut,需要通过它的 MethodMatcher 对方法进行匹配,其他的差不多。
DefaultAdvisorAdapterRegistry
org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry
,默认的 Advisor 适配器注册中心,主要是对 Advisor 中的 Advice 进行匹配处理
wrap 方法
@Override
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) { // Advisor 类型,直接返回
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) { // 非 Advice 接口,抛出异常
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) { // MethodInterceptor 类型,包装成 DefaultPointcutAdvisor 对象
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
// Check that it is supported.
// 检查该 Advice 类型是否支持
if (adapter.supportsAdvice(advice)) {
// 包装成 DefaultPointcutAdvisor 对象 返回
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
将 Advice 包装成 Advisor 对象
getInterceptors 方法
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
// <1> 获取 Advice 通知器
Advice advice = advisor.getAdvice();
/*
* <2> 若 Advice 是 MethodInterceptor 类型的,直接添加到 `interceptors`即可
* 例如 AspectJAfterThrowingAdvice、AspectJAfterAdvice、AspectJAroundAdvice
*/
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
/*
* <3> 通过 Advisor 适配器将 Advice 封装成对应的 MethodInterceptor 对象,并添加至 `interceptors`
* AspectJAfterReturningAdvice -> AfterReturningAdviceInterceptor
* AspectJMethodBeforeAdvice -> MethodBeforeAdviceInterceptor
*/
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
// <4> 没有对应的 MethodInterceptor 则抛出异常
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
// <5> 将 `interceptors` 转换成数组并返回
return interceptors.toArray(new MethodInterceptor[0]);
}
获取 Advisor 中的 MethodInterceptor 方法拦截器:
- 获取 Advice 通知器
- 若 Advice 是 MethodInterceptor 类型的,直接添加到
interceptors
即可- 例如
AspectJAfterThrowingAdvice、AspectJAfterAdvice、AspectJAroundAdvice
- 例如
- 通过 Advisor 适配器将 Advice 封装成对应的 MethodInterceptor 对象,并添加至
interceptors
AspectJAfterReturningAdvice -> AfterReturningAdviceInterceptor
AspectJMethodBeforeAdvice -> MethodBeforeAdviceInterceptor
- 没有对应的 MethodInterceptor 则抛出异常
- 将
interceptors
转换成数组并返回
可以看到,在 Spring AOP 的拦截处理中,使用的 Advice 都是 MethodInterceptor 方法拦截器
ReflectiveMethodInvocation
org.springframework.aop.framework.ReflectiveMethodInvocation
,代理对象目标方法的调用器,包含方法对应的 MethodInterceptor 拦截器链
构造函数
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
/** 代理对象 */
protected final Object proxy;
/** 目标对象 */
@Nullable
protected final Object target;
/** 目标方法 */
protected final Method method;
/** 方法入参 */
protected Object[] arguments;
/** 目标对象的 Class 对象 */
@Nullable
private final Class<?> targetClass;
/** 自定义属性 */
@Nullable
private Map<String, Object> userAttributes;
/** 方法的拦截器链路 */
protected final List<?> interceptorsAndDynamicMethodMatchers;
/** 当前已经执行完的拦截器的位置索引,执行完则执行目标方法 */
private int currentInterceptorIndex = -1;
protected ReflectiveMethodInvocation(
Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
this.targetClass = targetClass;
// 获取目标方法,如果是桥接方法则会找到目标方法
this.method = BridgeMethodResolver.findBridgedMethod(method);
// 对该方法参数进行适配处理(如果有必要)
this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
}
上面的属性通过上面的注释进行理解即可
invokeJoinpoint 方法
invokeJoinpoint()
方法,执行目标方法,如下:
@Nullable
protected Object invokeJoinpoint() throws Throwable {
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
} catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
} catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
基于反射执行目标方法
proceed 方法
proceed()
方法,方法调用器的执行,如下:
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// <1> 如果当前已经执行完的拦截器的位置索引就是最后一个,那么即可执行目标方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 执行目标方法(底层反射机制)
return invokeJoinpoint();
}
// <2> 按顺序获取拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
/**
* <3> 如果是 InterceptorAndDynamicMethodMatcher 类型,表示 MethodMatcher 在真正的执行时需要做一些检测
* 参考 {@link DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice }
*/
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// <3.1> 通过 MethodMatcher 对目标方法进行匹配
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
// 匹配通过,则执行这个拦截器,并传递当前对象
return dm.interceptor.invoke(this);
}
// <3.2> 否则,直接跳过这个拦截器
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// <4> 否则执行这个拦截器,并传递当前对象
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
方法调用器的执行过程如下:
- 如果
currentInterceptorIndex
(已经执行完的拦截器的位置索引)就是最后一个,那么即可执行目标方法,调用invokeJoinpoint()
方法 - 按顺序获取拦截器,先将
currentInterceptorIndex
自增1
- 如果是 InterceptorAndDynamicMethodMatcher 类型,表示 MethodMatcher 在真正的执行时需要做一些检测
- 通过 MethodMatcher 对目标方法进行匹配,匹配通过,则执行这个拦截器,并传递当前对象,调用
MethodInterceptor#invoke(this)
方法 - 否则,直接跳过这个拦截器,继续调用当前
proceed()
方法
- 通过 MethodMatcher 对目标方法进行匹配,匹配通过,则执行这个拦截器,并传递当前对象,调用
- 否则执行这个拦截器,并传递当前对象,调用
MethodInterceptor#invoke(this)
方法
上面第 3
步,为什么可能存在 InterceptorAndDynamicMethodMatcher 对象,返回前面的 DefaultAdvisorChainFactory 可以知晓答案
可以看到整个的方法调用过程是根据 MethodInterceptor 的顺序一个一个往下执行的,执行完了则执行目标方法。
那么你是否有疑问,为什么执行完最后一个拦截器才执行目标方法,后置通知器不是需要等目标方法执行后才进行处理的吗?其实你进入 Advice 实现的
MethodInterceptor#invoke(MethodInvocation)
方法就知道答案了,可查看下面这张图。
方法调用器的执行拦截器链图
到这里, JDK 动态代理创建的代理对象的拦截处理过程全部分析完了,做一个小的总结:
-
在拦截处理某个方法的时候,需要先通过 AdvisedSupport -> DefaultAdvisorChainFactory -> DefaultAdvisorAdapterRegistry 获取到能够应用于该方法的 Advisor 们,实例拿到的是它们对应的 MethodInterceptor 拦截器们,他们的顺序如上面这张图所示;
-
获取到了 MethodInterceptor 方法拦截器后,创建一个 ReflectiveMethodInvocation 方法调用器,进行方法的拦截处理,依次调用每个 MethodInterceptor 的
invoke(..)
方法,在最后执行目标方法; -
在上面这张图你可以看到不同 MethodInterceptor 的执行顺序,不过实际 Advice 的执行逻辑顺序是:
Around 前处理 > Before > Around 后处理 > After > AfterReturning|AfterThrowing
------------------------------------
CGLIB 动态代理
我们先来简单回顾一下 Spring AOP 中 CGLIB 动态代理创建代理对象的对象:
// CglibAopProxy.java
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
// ... 下面只展示部分代码
// <5> 创建 CGLIB 的增强类,并进行接下来的配置
Enhancer enhancer = createEnhancer();
// <5.1> 设置被代理的类
enhancer.setSuperclass(proxySuperClass);
// <5.2> 设置需要代理的接口(可能没有,不过都会加上 Spring 内部的几个接口,例如 SpringProxy)
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
// <5.3> 设置命名策略,默认生成的代理对象的名称中包含 '$$' 和 'BySpringCGLIB'
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
// <5.4> 获取回调接口,也就是 MethodInterceptor 方法拦截器
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// <5.5> 设置 Callback 过滤器,用于筛选出方法对应的 Callback 回调接口
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// Generate the proxy class and create a proxy instance.
// <6> 创建代理对象,CGLIG 字节码替身,创建目标类的子类
return createProxyClassAndInstance(enhancer, callbacks);
}
可以看到通过 Enhancer 创建代理对象,也就是目标类的子类,设置了一个 Callback 数组和 CallbackFilter 过滤器,CallbackFilter 用于获取目标方法对应的 Callback。这些内容都在上一篇《Spring AOP 自动代理(三)创建代理对象》文章中分析过,这里不再讲述,上一篇文章知道,CGLIB 进行 AOP 代理的通用拦截器是 DynamicAdvisedInterceptor 对象,那么接下来我们来看看这个拦截是怎么处理的。
DynamicAdvisedInterceptor
org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor
,CglibAopProxy 的私有静态内部类
intercept 方法
intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
方法,CGLIB 动态代理创建代理对象的通用拦截处理方法,如下:
private final AdvisedSupport advised;
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
// <1> 获取目标类的 TargetSource 对象,用于获取目标类
TargetSource targetSource = this.advised.getTargetSource();
try {
// <2> 如果 `expose-proxy` 属性为 `true`,则需要暴露当前代理对象
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// <2.1> 向 AopContext 中设置代理对象,并记录 ThreadLocal 之前存放的代理对象
// 这样一来,在 Advice 或者被拦截方法中可以通过 AopContext 获取到这个代理对象
oldProxy = AopContext.setCurrentProxy(proxy);
// <2.2> 标记这个代理对象被暴露了
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
// <3> 获取目标对象,以及它的 Class 对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// <4> 获取能够应用于该方法的所有拦截器(有序)
// 不同的 AspectJ 根据 @Order 排序
// 同一个 AspectJ 中的 Advice 排序:AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// <5> 如果拦截器链为空,则直接执行目标方法
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// <5.1> 参数适配处理
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// <5.2> 执行目标方法(反射),并获取返回结果
retVal = methodProxy.invoke(target, argsToUse);
}
// <6> 否则,需要根据拦截器链去执行目标方法
else {
// <6.1> 创建一个方法调用器,并将前面获取到的拦截器链传入其中,该对象就是 Joinpoint 对象
// <6.2> 执行目标方法,以及所有的 MethodInterceptor 方法拦截器(Advice 通知器),并获取返回结果
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
// <7> 对最终的返回结果进一步处理(返回结果是否需要为代理对象,返回结果是否不能为空)
retVal = processReturnType(proxy, target, method, retVal);
// <8> 返回 `retVal` 返回结果
return retVal;
}
finally {
// <9> 如果目标对象不为空,且 TargetSource 不是静态的(表示每次都得返回一个新的目标对象)
// 那么需要释放当前获取到的目标对象,通常情况下我们的单例 Bean 对应的都是 SingletonTargetSource,不需要释放
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
// <10> 如果暴露了当前代理对象,则需要将之前的代理对象重新设置到 ThreadLocal 中
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
整个的拦截处理过程:
- 获取目标类的 TargetSource 对象,用于获取目标类
- 如果
expose-proxy
属性为true
,则需要暴露当前代理对象- 向 AopContext 中设置代理对象,并记录 ThreadLocal 之前存放的代理对象
- 标记这个代理对象被暴露了
- 获取目标对象,以及它的 Class 对象
- 调用当前 AdvisedSupport 的
getInterceptorsAndDynamicInterceptionAdvice(..)
,获取能够应用于该方法的所有拦截器(有序)- 不同的 AspectJ 根据
@Order
排序 - 同一个 AspectJ 中的 Advice 排序:
AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
- 不同的 AspectJ 根据
- 如果拦截器链为空,则直接执行目标方法
- 参数适配处理
- 执行目标方法(反射),并获取返回结果
- 否则,需要根据拦截器链去执行目标方法
- 创建一个 CglibMethodInvocation 方法调用器,并将前面第
5
步获取到的拦截器链传入其中 - 执行方法调用器,会执行目标方法,以及所有的 MethodInterceptor 方法拦截器(Advice 通知器),并获取返回结果
- 创建一个 CglibMethodInvocation 方法调用器,并将前面第
- 对最终的返回结果进一步处理(返回结果是否需要为代理对象,返回结果是否不能为空)
- 返回
retVal
返回结果 - 如果目标对象不为空,且 TargetSource 不是静态的(表示每次都得返回一个新的目标对象),那么需要释放当前获取到的目标对象,通常情况下我们的单例 Bean 对应的都是 SingletonTargetSource,不需要释放
- 如果暴露了当前代理对象,则需要将之前的代理对象重新设置到 ThreadLocal 中
CGLIB 动态代理创建代理对象的通用拦截处理过程,和前面讲到的 JDK 动态代理创建的代理对象差不多,这里不再讲述。这里不同的是创建的方法调用器是 CglibMethodInvocation 对象,我们一起来看看这个对象
CglibMethodInvocation
org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation
,CglibAopProxy 的私有静态内部类
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
@Nullable
private final MethodProxy methodProxy;
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
Object[] arguments, @Nullable Class<?> targetClass,
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
// Only use method proxy for public methods not derived from java.lang.Object
// 设置代理方法对象
this.methodProxy = (Modifier.isPublic(method.getModifiers()) // 方法被 public 修饰
&& method.getDeclaringClass() != Object.class // 不是 Object 中的方法
&& !AopUtils.isEqualsMethod(method) // 不是 `equals` 方法
&& !AopUtils.isHashCodeMethod(method) // 不是 `hashCode` 方法
&& !AopUtils.isToStringMethod(method) // 不是 `toString` 方法
? methodProxy : null);
}
@Override
protected Object invokeJoinpoint() throws Throwable {
if (this.methodProxy != null) {
// 执行代理方法对象(反射)
return this.methodProxy.invoke(this.target, this.arguments);
}
else {
// 执行目标方法对象(反射)
return super.invokeJoinpoint();
}
}
}
可以看到它继承了 ReflectiveMethodInvocation 这个类,重写了 invokeJoinpoint()
执行目标方法的方法,区别在于 CGLIB 使用 MethodProxy 执行目标方法
总结
Spring AOP 有 JDK 动态代理 和 CGLIB 动态代理 两种创建代理对象的方式,前者通过 JdkDynamicAopProxy
创建代理对象,对应的 InvocationHandler
就是这个 JdkDynamicAopProxy
对象;后者通过 CglibAopPeoxy
创建代理对象,会设置了一个 Callback 数组和 CallbackFilter 过滤器,CallbackFilter 用于获取目标方法对应的 Callback,其中进行 AOP 代理的通用拦截器是 DynamicAdvisedInterceptor 方法拦截器。
JdkDynamicAopProxy
:先获取到能应用于方法的所有 MethodInterceptor(也就是 Advice),然后通过 ReflectiveMethodInvocation 方法调用器进行处理DynamicAdvisedInterceptor
:和上者处理逻辑差不多,区别是通过 CglibMethodInvocation 方法调用器进行处理,它重写了 ReflectiveMethodInvocation 执行目标方法的方法
至于 ReflectiveMethodInvocation 方法调用器的执行逻辑可以参考上面的讲解
到这里,关于 Spring AOP 自动代理,以及代理对象的拦截处理到这里已经全部讲述完了。其中肯定存在不少的问题,如有疑惑,可在留言区进行留言。