Spring AOP 源码分析:获得通知

Spring AOP 源码分析:获得通知

在文章 Spring AOP 处理流程概述 中,对 Spring AOP 有了一个整体认识。在文章 Spring AOP 源码分析:入门 中,对 Spring AOP 的相关入口做了分析。这篇文章就带大家看一看,Spring AOP 是如何获取通知的?

示例代码

如何阅读 Spring 源码?: 示例代码 中,已经给出了一个完整的 AOP 示例代码。为了节省篇幅,请直接参考那篇文章的示例代码,这里就不在赘述。

注册 Advice(通知/增强)

请根据 Spring AOP 源码分析:入门 中提到的关键方法入口处,打上断点,开始调试。

首先,需要明确一点的是:对于切面(使用 @Aspect 注解标注过的类)在 Spring 容器中,也是被统一f封装为 BeanDefinition 实例的,也需要通过一个方式,将其注册到 Spring 容器中。比如,就像 示例代码 那样,通过 ImportSelector 方式,使用类名,将其注册到容器中。这样,就可以利用 Spring 容器对 Bean 的 API 来统一处理了。

Advice(通知/增强)几乎是在意想不到的地方完成注册的:在第一次调用 AbstractAutoProxyCreator#postProcessBeforeInstantiation 方法时,通过 AspectJAwareAdvisorAutoProxyCreator#shouldSkip 方法,完成了切面的注册。下面,我们对这个过程抽丝剥茧,逐步分析。

先来看看 findCandidateAdvisors 方法:

AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
@Override
protected List<Advisor> findCandidateAdvisors() {
  // Add all the Spring advisors found according to superclass rules.
  //当使用注解方式配置AOP的时候并不是丢弃了对XML配置的支持
  //在这里调用父类方法加载配置文件中的AOP声明
  List<Advisor> advisors = super.findCandidateAdvisors();
  // Build Advisors for all AspectJ aspects in the bean factory.
  if (this.aspectJAdvisorsBuilder != null) {
    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
  }
  return advisors;
}

查找 XML 配置的 Advisor

正如上面注释所示,会先通过 super.findCandidateAdvisors() 先获取父类方法加载的切面声明:

AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
protected List<Advisor> findCandidateAdvisors() {
  Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
  return this.advisorRetrievalHelper.findAdvisorBeans();
}

直接看 advisorRetrievalHelper.findAdvisorBeans() 方法:

BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
public List<Advisor> findAdvisorBeans() {
  // Determine list of advisor bean names, if not cached already.
  String[] advisorNames = this.cachedAdvisorBeanNames;
  if (advisorNames == null) {
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the auto-proxy creator apply to them!
    // 在 beanNamesForTypeIncludingAncestors 方法中,通过遍历所有 Bean 名称来选取合适的对对象,
    // 查找的是通过 XML 配置的 <aop:advisor/> Bean。并不会把 @Aspect 标注的类给选出来。
    advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this.beanFactory, Advisor.class, true, false);
    this.cachedAdvisorBeanNames = advisorNames;
  }
  if (advisorNames.length == 0) {
    return new ArrayList<>();
  }

  List<Advisor> advisors = new ArrayList<>();
  for (String name : advisorNames) {
    if (isEligibleBean(name)) {
      if (this.beanFactory.isCurrentlyInCreation(name)) {
        if (logger.isTraceEnabled()) {
          logger.trace("Skipping currently created advisor '" + name + "'");
        }
      }
      else {
        try {
          advisors.add(this.beanFactory.getBean(name, Advisor.class));
        }
        catch (BeanCreationException ex) {
          Throwable rootCause = ex.getMostSpecificCause();
          if (rootCause instanceof BeanCurrentlyInCreationException) {
            BeanCreationException bce = (BeanCreationException) rootCause;
            String bceBeanName = bce.getBeanName();
            if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
              if (logger.isTraceEnabled()) {
                logger.trace("Skipping advisor '" + name +
                    "' with dependency on currently created bean: " + ex.getMessage());
              }
              // Ignore: indicates a reference back to the bean we're trying to advise.
              // We want to find advisors other than the currently created bean itself.
              continue;
            }
          }
          throw ex;
        }
      }
    }
  }
  return advisors;
}

这里通过 BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class<?>, boolean, boolean) 方法来查找 Advisor。整个过程,就是针对 BeanFactory 递归调用其父 BeanFactory,遍历所有的 Bean 名称,查找类型为 Advisor 的 Bean 名称,然后调用 beanFactory.getBean(name, Advisor.class),来获得对应的 Advisor Bean 并返回。

上面介绍了查找 XML 配置的 Advisor 过程。

查找通过注解配置的 Advisor

我们回到 AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors 方法中, BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
public List<Advisor> buildAspectJAdvisors() {
  List<String> aspectNames = this.aspectBeanNames;

  if (aspectNames == null) {
    synchronized (this) {
      aspectNames = this.aspectBeanNames;
      if (aspectNames == null) {
        List<Advisor> advisors = new ArrayList<>();
        aspectNames = new ArrayList<>();
        //获取所有的beanName
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.beanFactory, Object.class, true, false);
        //循环所有的beanName找出对应的增强方法
        for (String beanName : beanNames) {
          //不合法的bean则略过,由子类定义规则,默认返回true
          if (!isEligibleBean(beanName)) {
            continue;
          }
          // We must be careful not to instantiate beans eagerly as in this case they
          // would be cached by the Spring container but would not have been weaved.
          //获取对应的bean的类型
          Class<?> beanType = this.beanFactory.getType(beanName);
          if (beanType == null) {
            continue;
          }
          //如果存在Aspect注解
          if (this.advisorFactory.isAspect(beanType)) {
            aspectNames.add(beanName);
            AspectMetadata amd = new AspectMetadata(beanType, beanName);
            if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
              MetadataAwareAspectInstanceFactory factory =
                  new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
              //解析标记AspectJ注解中的增强方法
              List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
              if (this.beanFactory.isSingleton(beanName)) {
                this.advisorsCache.put(beanName, classAdvisors);
              }
              else {
                this.aspectFactoryCache.put(beanName, factory);
              }
              advisors.addAll(classAdvisors);
            }
            else {
              // Per target or per this.
              if (this.beanFactory.isSingleton(beanName)) {
                throw new IllegalArgumentException("Bean with name '" + beanName +
                    "' is a singleton, but aspect instantiation model is not singleton");
              }
              MetadataAwareAspectInstanceFactory factory =
                  new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
              this.aspectFactoryCache.put(beanName, factory);
              advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
          }
        }
        this.aspectBeanNames = aspectNames;
        return advisors;
      }
    }
  }

  if (aspectNames.isEmpty()) {
    return Collections.emptyList();
  }
  List<Advisor> advisors = new ArrayList<>();
  for (String aspectName : aspectNames) {
    List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
    if (cachedAdvisors != null) {
      advisors.addAll(cachedAdvisors);
    }
    else {
      MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
      advisors.addAll(this.advisorFactory.getAdvisors(factory));
    }
  }
  return advisors;
}

这里的逻辑比上面要简单清晰好多:查找出所有的 Bean 名称,然后选出类型标注了 @Aspect 注解的 Bean 类型,把 Bean 名称添加到 BeanFactoryAspectJAdvisorsBuilder#aspectBeanNames 实例变量中;根据类型信息,使用反射针对符合添加的方法,构建 Advisor 对象(实现类为 InstantiationModelAwarePointcutAdvisorImpl),然后将其加入到 BeanFactoryAspectJAdvisorsBuilder#advisorsCache 变量中。

值得注意的是根据通知的类型,创建不同的 Advice 对象,也是在上面的这个过程中,在 ReflectiveAspectJAdvisorFactory#getAdvice 方法中完成的。

经过上面的处理,所有对应的 Advice(通知/增强)都会被查找出来。接下来,我们看一看如何针对特定的 Bean 选择出合适的 Advice(通知/增强)的。

这里说“注册”其实意思不太正确。 Advice(通知/增强)没有什么注册一说,它只是被解析后缓存了起来。下次再使用时,就不需要解析了。

选取 Advice(通知/增强)

上面解析后的 Advice(通知/增强)都被存放在了 BeanFactoryAspectJAdvisorsBuilder#Map<String, List<Advisor>> advisorsCache 变量中。所以,从这里拿到所有通知后再去做筛选。

Spring Bean 生命周期概述 中已经强调过了,AOP 代理的创建是在执行 BeanPostProcessor#postProcessAfterInitialization,也就是 AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 方法中。

Spring AOP 源码分析:入门 中提到`getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource)` 获取了所有符合条件的增强信息。

结合上面两点,找到对应的方法就是 AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean。但是,这个方法几乎没啥代码,而是把处理全部委托给了 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors 方法来处理。所以,可以直接看这个方法:

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;
}

重点在 findAdvisorsThatCanApply 方法上,从方法名上来看,这似乎是要查找可用的 Advisor。来看一下具体实现:

AopUtils#findAdvisorsThatCanApply
// 找到合适的 advisors,引介增强在前,其他普通增强在后
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
  if (candidateAdvisors.isEmpty()) {
    return candidateAdvisors;
  }
  List<Advisor> eligibleAdvisors = new ArrayList<>();
  // 首先处理引介增强
  for (Advisor candidate : candidateAdvisors) {
    if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
      eligibleAdvisors.add(candidate);
    }
  }
  boolean hasIntroductions = !eligibleAdvisors.isEmpty();
  for (Advisor candidate : candidateAdvisors) {
    if (candidate instanceof IntroductionAdvisor) {
      // already processed
      continue;
    }
    // 这里来解析切入点表达式
    if (canApply(candidate, clazz, hasIntroductions)) {
      eligibleAdvisors.add(candidate);
    }
  }
  return eligibleAdvisors;
}

经过多个重载 canApply 方法的来回传递,最后由如下方法来进行处理:

AopUtils#canApply(Pointcut, Class<?>, boolean)
// 表达式被解析成 AspectJExpressionPointcut 对象
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
  Assert.notNull(pc, "Pointcut must not be null");
  // 这里先判断类型是否匹配。交给 AspectJ 来完成了,不再深究。
  if (!pc.getClassFilter().matches(targetClass)) {
    return false;
  }

  MethodMatcher methodMatcher = pc.getMethodMatcher();
  if (methodMatcher == MethodMatcher.TRUE) {
    // No need to iterate the methods if we're matching any method anyway...
    return true;
  }

  IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
  if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
    introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
  }

  Set<Class<?>> classes = new LinkedHashSet<>();
  if (!Proxy.isProxyClass(targetClass)) {
    classes.add(ClassUtils.getUserClass(targetClass));
  }
  classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

  for (Class<?> clazz : classes) {
    Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
    // 针对每个方法,判断是否符合表达式要求
    for (Method method : methods) {
      if (introductionAwareMethodMatcher != null ?
          // 匹配操作都交给了 AspectJ 来完成,不再深究
          introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
          methodMatcher.matches(method, targetClass)) {
        return true;
      }
    }
  }

  return false;
}

结果类型和方法的双重筛选后,就可以把符合条件的 Advice(通知/增强)给选择出来了。下一篇文章,来介绍一下如果创建代理类: Spring AOP 源码分析:创建代理(一)Spring AOP 源码分析:创建代理(二)