下载APP

Spring 源码阅读 55:查找 Advisor 的过程总结

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

基于 Spring Framework v5.2.6.RELEASE

概述

在 Spring 通过后处理器为 Bean 实例创建代理对象时,有两个关键的步骤。一个是从容器中找到所有与当前 Bean 实例匹配的 Advisor 也就是增强方法;另一个是代理对象的创建。之前,通过5篇文章,深入分析了 Spring 为目标 Bean 实例查找 Advisor 的过程,这一过程主要在 AbstractAdvisorAutoProxyCreator 类的findEligibleAdvisors方法中完成。本文将对这个方法中已经分析过的部分做一个整体梳理和总结,并分析查询到 Advisor 之后,后续部分的逻辑。

查找所有的 Advisor

我们从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;
}

首先是第一步,通过findCandidateAdvisors方法查找容器中所有的 Advisor,这里分两种情况。

查找基于 XML 切面配置的 Advisor

第一种情况是通过 XML 配置文件来配置切面,这种情况下,在 Spring 上下文初始化的时候,会解析 XML 文件中的配置,通过自定义标签解析的方式,解析出 XML 文件中切面配置相关的内容,并封装 Advisor 对象,注册到容器中。

在后处理器查找 Advisor 的时候,只需要从容器中获取到所有 Advisor 类型的 Bean 实例就可以了。

查找基于注解配置的 Advisor

第二种情况是对于通过注解配置的切面信息,这种方式是基于 Java 类来配置的,在 Spring 上下文初始化的时候,这些类只会被注册为一个普通的 Bean,因此查找这些配置中的 Advisor 的过程会相对复杂一点。

首先,Spring 会从容器中注册的所有 Bean 中,获取到被标记了@Aspect注解的类型的 Bean。然后,从这些类信息中找到切入点的定义和增强方法的定义。最后,再将所有的增强方法封装成对应的 Advisor。

查找适配的 Advisor

在找到了所有的 Advisor 之后,就到了findEligibleAdvisors方法的第二步,也就是从这些 Advisor 中,筛选出与当前的 Bean 实例匹配的 Advisor,这个工作由findAdvisorsThatCanApply方法来完成。

在 Spring AOP 中,增强方法与目标的匹配都是通过切入点表达式来配置的,因此,匹配工作的核心也是切入点表达式。每一个 Advisor 都有对应的切入点表达式,因此匹配 Advisor 的过程,就是从目标 Bean 中,获取到所有声明的方法,与每一个 Advisor 进行匹配。因为 Spring AOP 创建代理的目标是一个对象,因此,一个 Advisor 能够匹配目标类型中的任意一个方法都可以被筛选为适配的 Advisor。

这个过程执行完,就得到了一个与当前的目标 Bean 得类型对应的 Advisor 列表,eligibleAdvisors

扩展 Advisor

总结完前面的逻辑,我们接着看findEligibleAdvisors方法后续的部分。

extendAdvisors(eligibleAdvisors);

我们找到这个方法的代码。

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

它其实是一个空的方法,用来留给子类扩展,而我们用到的后处理器类型,正好重写了这个方法。

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

这段逻辑在 AspectJAwareAdvisorAutoProxyCreator 中,方法中只有一句方法调用,我们进入 AspectJProxyUtils 的makeAdvisorChainAspectJCapableIfNecessary中。

// 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方法的条件,则在 Advisor 列表的起始位置,添加一个新的 Advisor。

我们看一下isAspectJAdvice方法做了哪些判断。

private static boolean isAspectJAdvice(Advisor advisor) {
   return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
         advisor.getAdvice() instanceof AbstractAspectJAdvice ||
         (advisor instanceof PointcutAdvisor &&
               ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}

很显然,我们平时开发中配置的增强逻辑都是符合这个条件的。接下来,再看一下添加到列表起始位置的 Advisor 是什么,找到 ExposeInvocationInterceptor 类的ADVISOR常量。

// org.springframework.aop.interceptor.ExposeInvocationInterceptor#ADVISOR
public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
   @Override
   public String toString() {
      return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
   }
};

// org.springframework.aop.interceptor.ExposeInvocationInterceptor#INSTANCE
public static final ExposeInvocationInterceptor INSTANCE = new ExposeInvocationInterceptor();

其实就是将 ExposeInvocationInterceptor 的单例对象封装成了一个 DefaultPointcutAdvisor。从 ExposeInvocationInterceptor 的类注释中可以了解到,它一般需要添加到 Advisor 列表的起始位置,作用是将 Spring AOP 的方法调用上下文通过 ThreadLocal 公开,后续遇到有关的逻辑,我们再做详细的分析。

这就是extendAdvisors方法要执行的逻辑,我们再次回到findEligibleAdvisors方法中。

排序

if (!eligibleAdvisors.isEmpty()) {
   eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;

最后就是对 Advisor 列表进行排序,作为findEligibleAdvisors方法的结果返回。

这里排序的一句就是根据每一个 Advisor 是否实现了 PriorityOrdered 接口以及接口提供的优先级来排序,这种排序方式在 Spring 中应用广泛,这里就不深入介绍了。

至此,findEligibleAdvisors方法的全部内容就分析完了,用于创建 AOP 代理的后处理器通过这个方法,为当前处理的 Bean 实例找到了所有匹配的 Advisor,接下来就该为它创建代理对象了。

总结

本文完整总结和分析了findEligibleAdvisors方法为目标 Bean 实例查找所有适配的 Advisor 的过程,得到这些 Advisor 之后,就可以开始为需要被代理的 Bean 实例创建代理对象了。

在线举报