Spring 源码阅读 70:容易被忽略的 ExposeInvocationInterceptor

738 阅读4分钟

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

基于 Spring Framework v5.2.6.RELEASE

相关阅读:Spring 源码阅读 61:基于 JDK 动态代理的 AOP 代理回调方法 invoke 分析

接上一篇:Spring 源码阅读 69:MethodInterceptor 分析

概述

之前的三篇文章,分析了 JdkDynamicAopProxy 的invoke方法,如何获取到拦截器链,并通过 ReflectiveMethodInvocation 的proceed方法执行拦截器链的增强方法和目标方法的逻辑。在拦截器链的增强方法的执行逻辑中,分别分析了五种增强类型对应的拦截器中的invoke方法的逻辑。

但其实,在拦截器链中,还有一个不是这五种增强类型对应的拦截器,它就是 ExposeInvocationInterceptor,这个拦截器并不是与我们配置的增强方法对应的,所以很容易被忽略。

这个拦截器类型,其实我们之前的源码分析中是见过的,我们先回顾一下,它是什么时候被注册的。

ExposeInvocationInterceptor 的注册

Spring AOP 的特性,是通过在后处理器中创建目标 Bean 对象的代理对象来实现的。当 Bean 对象初始化完成之后,会在后处理器中查找与当前 Bean 对象的类型是配的增强逻辑,这个查找的逻辑是由 AbstractAdvisorAutoProxyCreator 的findEligibleAdvisors完成的。

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
} 

在这个方法中,前两行代码的作用是找出 Spring 容器中所有的增强以及从这些增强中筛选出与当前 Bean 得类型匹配的增强。之后,通过extendAdvisors方法,对这个增强列表进行了扩展操作。

// org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#extendAdvisors
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
}

这个方法在 AbstractAdvisorAutoProxyCreator 中是一个空方法,但是,无论是 XML 配置的切面配置,还是注解配置的切面配置,其对应的后处理器实现类,并不是 AbstractAdvisorAutoProxyCreator 本身,而是它的子类,在子类 AspectJAwareAdvisorAutoProxyCreator 中,重写了这个方法,逻辑如下。

// org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#extendAdvisors
@Override
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
   AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

这里执行了一个方法,我们进入这个方法。

// org.springframework.aop.aspectj.AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
   // Don't add advisors to an empty list; may indicate that proxying is just not required
   if (!advisors.isEmpty()) {
      boolean foundAspectJAdvice = false;
      for (Advisor advisor : advisors) {
         // Be careful not to get the Advice without a guard, as this might eagerly
         // instantiate a non-singleton AspectJ aspect...
         if (isAspectJAdvice(advisor)) {
            foundAspectJAdvice = true;
            break;
         }
      }
      if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
         advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
         return true;
      }
   }
   return false;
}

可以看到这个方法的逻辑并不复杂,其主要作用是,如果之前获取到的 Advisor 列表包含符合isAspectJAdvice的判断逻辑(我们配置的增强逻辑都符合这个条件),则向列表的开头增加一个ExposeInvocationInterceptor.ADVISOR

下面我们看一下这里添加的ExposeInvocationInterceptor.ADVISOR

ExposeInvocationInterceptor 是什么

我们直接找到 ExposeInvocationInterceptor 类和ADVISOR常量的定义。

public final class ExposeInvocationInterceptor implements MethodInterceptor, PriorityOrdered, Serializable {

   /** Singleton instance of this class. */
public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();

   /**
* Singleton advisor for this class. Use in preference to INSTANCE when using
* Spring AOP, as it prevents the need to create a new Advisor to wrap the instance.
*/
public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
      @Override
      public String toString() {
         return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
      }
   };
   
   // 省略其他代码
   
 }

从以上的代码中可以看出:

  • ExposeInvocationInterceptor 是一个 MethodInterceptor 的实现类,也就是和我们之前分析过的五种增强类型对应的拦截器一样,通过invoke方法来实现其拦截器的逻辑。
  • ADVISOR常量,其实就是一个包含了一个 ExposeInvocationInterceptor 单例对象的 DefaultPointcutAdvisor 对象。

了解了这些,我们可以得出结论,这个拦截器其实和五种增强类型对应的拦截器很相似,因此,要分析它的作用,只要看它的invoke方法就行了。

ExposeInvocationInterceptor 的执行

下面看它的invoke方法源码。

// org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   MethodInvocation oldInvocation = invocation.get();
   invocation.set(mi);
   try {
      return mi.proceed();
   }
   finally {
      invocation.set(oldInvocation);
   }
}

方法中除了执行mi的proceed方法外,频繁操作了invocation成员变量,我们看一下这个它是什么。

private static final ThreadLocal<MethodInvocation> invocation =
      new NamedThreadLocal<>("Current AOP method invocation");

它是一个 ThreadLocal 变量。在invoke方法中,会先获取到invocation中的内容,暂存在oldInvocation变量,然后把mi放到invocation中。当miproceed方法执行完之后,再将invocation中最初保存的内容还原。

因为 ExposeInvocationInterceptor 是拦截器链中第一个拦截器,因此,它的作用是,在进入拦截器执行逻辑的时候,将 MethodInvocation 方法调用对象暴露到 ThreadLocal 中,在拦截器链执行完之后再还原。根据之前的分析,我们也能知道,这里参数传入的值,就是在 JdkDynamicAopProxy 的invoke方法中创建的 ReflectiveMethodInvocation 对象。

这也是 ExposeInvocationInterceptor 过滤器起到的作用。

总结

本文分析了 Spring AOP 拦截器链中的一个特殊拦截器 ExposeInvocationInterceptor 的注册的时机以及它的作用。至此,基于 JDK 的 AOP 代理拦截器链执行的逻辑就分析完了。