Spring 源码阅读 72:基于 CGLIB 的 AOP 代理拦截器的查找与执行

167 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第34天,点击查看活动详情

基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 60:通过 JDK 动态代理或者 CGLIB 创建 AOP 代理对象

接上一篇:# Spring 源码阅读 71:DynamicAdvisedInterceptor 分析

概述

上一篇分析了基于 CGLIB 的 AOP 代理对象执行 AOP 增强逻辑的入口,也就是 DynamicAdvisedInterceptor 的intercept方法,本文来进入这个方法,分析其执行增强逻辑的原理。

DynamicAdvisedInterceptor 的 intercept 方法

首先看这个方法的源码。

方法虽然很长,但是真正执行逻辑的代码并不多。

Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();

方法的一开始会声明一些变量,这些变量会在后面的流程中看到,其中包括从advised属性中获取到的被代理的目标对象。advised成员变量是在创建 DynamicAdvisedInterceptor 是通过构造方法的参数传入的。

后续的流程会进入一个try语句块。

if (this.advised.exposeProxy) {
   // Make invocation available if necessary.
   oldProxy = AopContext.setCurrentProxy(proxy);
   setProxyContext = true;
}

这个if语句块中,会将代理对象放到 AopContext 上下文中,不过默认情况下,this.advised.exposeProxy的值是false,因此这里的逻辑不会被执行。

拦截器的查找

// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

接下来会进入一个重要的步骤,就是根据目标类型和当前调用的方法,查找拦截器链。如果你读过之前关于基于 JDK 动态代理的 AOP 代理的原理,就会发现这个用来查找拦截器链的方法很熟悉。其实,这个方法跟 JdkDynamicAopProxy 的invoke方法中调用的getInterceptorsAndDynamicInterceptionAdvice方法是同一个方法。

关于这个方法的原理,我之前写过 4 篇文章来详细介绍,可以阅读之前的文章来了解其中的原理。

传送门:如何获取拦截器链:1234

拦截器的执行

// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
   // 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.
   Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
   retVal = methodProxy.invoke(target, argsToUse);
}
else {
   // We need to create a method invocation...
   retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}

接下来就是对获取到的拦截器链进行判断,如果拦截器链是空的,且方法是public修饰的,则不需要执行拦截器的逻辑。在if语句块中,首先通过 AopProxyUtils 类的adaptArgumentsIfNecessary方法,对调用方法的参数进行处理,这里主要是在有必要的情况下,对方法参数列表中的可变参数进行类型适配。然后,通过methodProxyinvoke方法直接调用目标方法。

如果拦截器链不为空,那么则会通过构造方法创建一个 CglibMethodInvocation 对象,然后调用它的proceed方法,执行拦截器链的增强逻辑。

我们先看一下 CglibMethodInvocation 类。

可以看到,它是 ReflectiveMethodInvocation 的子类,而 ReflectiveMethodInvocation 是基于 JDK 动态代理的 AOP 代理回调方法中用来执行拦截器链逻辑的类型,因此可以推测,这两种代理方式的拦截器链执行逻辑应该也大同小异。

接下来看一下 CglibMethodInvocation 的类定义和构造方法。

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()) &&
            method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
            !AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
            methodProxy : null);
   }
   
   // 省略其他代码
   
}

它是 CglibAopProxy 的一个静态内部类,在构造方法中,调用了父类的构造方法,也就是 ReflectiveMethodInvocation 的构造方法,此外,还初始化了methodProxy变量。methodProxy初始化的逻辑也比较简单,主要是确保被代理的方法是public修饰的不是声明在 Object 中的方法,且不是equalshashCodetoString这三个方法。

对父类构造方法的调用主要是一些属性的初始化,之前介绍 ReflectiveMethodInvocation 的时候,已经介绍过,这里不再赘述。

了解完构造方法之后,再看其执行拦截器链逻辑的proceed方法。

// org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation#proceed
@Override
@Nullable
public Object proceed() throws Throwable {
   try {
      return super.proceed();
   }
   catch (RuntimeException ex) {
      throw ex;
   }
   catch (Exception ex) {
      if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
         throw ex;
      }
      else {
         throw new UndeclaredThrowableException(ex);
      }
   }
}

其核心逻辑只有一行,就是调用父类的proceed方法,也就是 ReflectiveMethodInvocation 的proceed方法,到这里,接下来的逻辑就跟基于 JDK 动态代理的 AOP 代理回调执行拦截器链的逻辑相同了,在之前的文章中,也详细分析过这个方法的执行逻辑,你可以通过之前的文章来了解其中的细节。

传送门:ReflectiveMethodInvocation 分析MethodInterceptor 分析

总结

本文分析了基于 CGLIB 的 AOP 代理如何查找和执行拦截器链,其主要的逻辑在 DynamicAdvisedInterceptor 的intercept方法执行。对于拦截器链的查找和执行逻辑,与基于 JDK 动态代理的 AOP 代理的执行逻辑共用了大部分的代码。