Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator【一万字】

  基于最新Spring 5.x,详细介绍了基于注解的Sping AOP原理,包括对于< aop:aspectj-autoproxy/>AOP标签的解析、Aspect切面注解的解析、@EnableAspectJAutoProxy注解的解析、AnnotationAwareAspectJAutoProxyCreator以及Sping Boot中AOP的自动配置原理,这一切都是在前几篇文章之上进行讲解的!

  此前的几篇文章我们学习了基于XML的Sping AOP的< aop:config/>标签解析、创建代理、调用代理的一整套流程。
  现在我们来学习基于注解的Sping AOP流程,我们在最开始就说过,XML和注解的Spring AOP配置最终都输殊途同归的,它们之间会调用到很多共同的方法,如果我们学习了此前基于XML的几篇文章,那么学习基于注解的Spring AOP流程就会非常的简单了,本文同样默认大家都已经掌握了基于XML的AOP源码,对于某些共同调用的方法不再赘述!

Spring AOP源码 系列文章

Spring AOP源码(1)—<aop:config/>AOP配置标签解析

Spring AOP源码(2)—AspectJAwareAdvisorAutoProxyCreator创建代理对象

Spring AOP源码(3)—invoke代理方法的调用与执行增强

Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator

Spring AOP源码(5)—DefaultAdvisorAutoProxyCreator自动代理创建者

文章目录

  • Spring AOP源码 系列文章
  • 1 < aop:aspectj-autoproxy/>AOP标签解析
    • 1.1 AspectJAutoProxyBeanDefinitionParser.parse
      • 1.1.1 registerAspectJAnnotationAutoProxyCreatorIfNecessary注册自动代理创建者
      • 1.1.2 extendBeanDefinition解析< aop:include/>子标签
  • 2 AnnotationAwareAspectJAutoProxyCreator自动代理创建者
    • 2.1 findCandidateAdvisors查找候选Advisors
      • 2.1.1 BeanFactoryAspectJAdvisorsBuilder构建者
      • 2.1.2 AspectJAdvisorFactory工厂
      • 2.1.3 buildAspectJAdvisors构建AspectJ通知器
        • 2.1.3.1 isEligibleAspectBean是否是合格的切面bean
        • 2.1.3.2 isAspect是否是切面bean
        • 2.1.3.3 BeanFactoryAspectInstanceFactory切面实例工厂
        • 2.1.3.4 getAdvisors获取Advisors通知器
          • 2.1.3.4.1 validate校验切面类
          • 2.1.3.4.2 getAdvisorMethods获取全部通知方法
          • 2.1.3.4.3 getAdvisor根据通知方法尝试获取通知器
            • 2.1.3.4.3.1 getPointcut获取切入点表达式对象
            • 2.1.3.4.3.1.1 findAspectJAnnotationOnMethod查找AspectJ 注解
            • 2.1.3.4.3.2 InstantiationModelAwarePointcutAdvisorImpl通知器
            • 2.1.3.4.3.2.1 instantiateAdvice实例化Advice通知
          • 2.1.3.4.4 getDeclareParentsAdvisor获取引介增强通知器
  • 3 @EnableAspectJAutoProxy注解解析
    • 3.1 @EnableAspectJAutoProxy注解的源码
    • 3.2 AspectJAutoProxyRegistrar自动代理注册员
  • 4 补充:Spring Boot 的自动配置
  • 5 总结

1 < aop:aspectj-autoproxy/>AOP标签解析

  < aop:aspectj-autoproxy/>标签用于开启对于AOP注解的支持,我们先看看这个标签的解析流程,它使用AspectJAutoProxyBeanDefinitionParser解析器解析。
  如果我们使用这个标签,那么虽然我们可以使用AOP注解,但是仍然会引入XML文件,后面我们马上会学习基于纯注解和Java配置的Spring AOP原理,这里介绍的< aop:aspectj-autoproxy/>标签可以算作是一个补充和入门!

1.1 AspectJAutoProxyBeanDefinitionParser.parse

  < aop:aspectj-autoproxy/>标签的解析和此前学习的< aop:config/>标签的解析有很多相似的地方,并且更加简单。对于该标签的解析,主要目的有两个:

  1. 尝试解析< aop:aspectj-autoproxy/>标签的属性,并且注册或者升级一个名为"org.springframework.aop.config.internalAutoProxyCreator",类型为AnnotationAwareAspectJAutoProxyCreator的自动代理创建者的bean定义。
  2. 解析< aop:include/>子标签,扩展自动代理创建者的bean定义。该标签用于指定一些模式从而根据beanName筛选切面类,这个标签很少被使用。
/**
 1. AspectJAutoProxyBeanDefinitionParser的方法
 2. 

3. 解析标签,开启AOP注解支持 4. 5. @param element 标签元素 6. @param parserContext 解析上下文 7. @return 默认null */ @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { /* * 1 尝试解析标签的属性,并且注册或者升级一个名为"org.springframework.aop.config.internalAutoProxyCreator" * 类型为AnnotationAwareAspectJAutoProxyCreator的自动代理创建者的bean定义 */ AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); /* * 2 解析子标签,扩展自动代理创建者的bean定义 * 该标签用于指定一些模式从而根据beanName筛选切面类,这个标签很少被使用 */ extendBeanDefinition(element, parserContext); return null; }

1.1.1 registerAspectJAnnotationAutoProxyCreatorIfNecessary注册自动代理创建者

  分为三步:

  1. 调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary,尝试注册或者升级一个名为"org.springframework.aop.config.internalAutoProxyCreator",类型为AnnotationAwareAspectJAutoProxyCreator的自动代理创建者的bean定义。
  2. 调用useClassProxyingIfNecessary,解析proxy-target-class与expose-proxy属性。
  3. 调用registerComponentIfNecessary注册组件,这里的注册是指存放到外层方法新建的CompositeComponentDefinition对象的内部集合中或者广播事件,而不是注册到注册表中。
/**
 * AopNamespaceUtils的方法
 * 

* 如有必要,注册AnnotationAwareAspectJAutoProxyCreator */ public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) { /* * 1 尝试注册或者升级一个名为"org.springframework.aop.config.internalAutoProxyCreator" * 类型为AnnotationAwareAspectJAutoProxyCreator的自动代理创建者的bean定义 */ BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); /* * 2 尝试解析标签的proxy-target-class与expose-proxy属性 * proxy-target-class用于设置代理模式,默认是优先JDK动态代理,其次CGLIB代理,可以指定为CGLIB代理 * expose-proxy用于暴露代理对象,主要用来解决同一个目标类的方法互相调用时代理不生效的问题 */ useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); /* * 3 注册组件,这里的注册是指存放到外层新建的CompositeComponentDefinition对象的内部集合中或者广播事件,而不是注册到注册表中 */ registerComponentIfNecessary(beanDefinition, parserContext); }

  可以看到,除了第一个方法之外,其他方法甚至主要逻辑都是和前面学习的registerAspectJAutoProxyCreatorIfNecessary方法非常相似。
  当我们进入AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,我们发现还是和此前学习的一样,只不是这里的第一个参数变成了AnnotationAwareAspectJAutoProxyCreator.class

@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    /*
     * 继续调用registerOrEscalateApcAsRequired方法
     * 由于解析的标签,因此第一个参数是AnnotationAwareAspectJAutoProxyCreator.class
     */
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

  结合此前说的“容器最终只会创建一个自动代理创建者bean定义,采用优先级最高的自动代理创建者的类型”。因此,如果同时存在< aop:aspectj-autoproxy/>和< aop:config/>标签,那么最终的自动代理创建者的类型就是AnnotationAwareAspectJAutoProxyCreator。
  AnnotationAwareAspectJAutoProxyCreator是AspectJAwareAdvisorAutoProxyCreator的子类,他继承了父类的全部特点,因此它们大量采用同一个方法构建代理以及执行代理。同时AnnotationAwareAspectJAutoProxyCreator还扩展了功能,那就是支持通过相关AspectJ注解配置AOP。
Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator【一万字】_第1张图片

1.1.2 extendBeanDefinition解析< aop:include/>子标签

  该方法用于解析< aop:include/>子标签,扩展前面的自动代理创建者的bean定义。
我们可以通过配置< aop:include/>标签,来指定只有具有指定格式的@Aspect切面类才会生效, 所有< aop:include/>标签配置的name属性值,将会在创建自动代理创建者实例时转换为Pattern对象并封装成一个List< Pattern >集合,赋给它的includePatterns属性,因此,这里的name值应该是一个正则表达式,这在AnnotationAwareAspectJAutoProxyCreator的setIncludePatterns方法中很明显的就能看出来!
  因此,我们可以通过配置< aop:include/>标签,来配置只有具有指定格式的@Aspect切面类才会生效(通过判断beanName是否匹配格式,在后面的isEligibleAspectBean方法有讲解)。但是,这个标签很少被使用到。

/**
 * 内部管理的自动代理创建者的 bean 名称
 */
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
        "org.springframework.aop.config.internalAutoProxyCreator";


/**
 * AspectJAutoProxyBeanDefinitionParser的方法
 * 

* 解析子标签,扩展bean定义 * * @param element 标签元素 * @param parserContext 解析上下文 */ private void extendBeanDefinition(Element element, ParserContext parserContext) { //获取在前面注册的自动代理创建者的bean定义 BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); //如果当前标签具有子节点 if (element.hasChildNodes()) { //添加包括的模式 addIncludePatterns(element, parserContext, beanDef); } } /** * AspectJAutoProxyBeanDefinitionParser的方法 *

* 解析子标签,扩展bean定义 * * @param element 标签元素 * @param parserContext 解析上下文 * @param beanDef 自动代理创建者的bean定义 */ private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) { ManagedList<TypedStringValue> includePatterns = new ManagedList<>(); NodeList childNodes = element.getChildNodes(); //遍历子节点 for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); //处理标签节点 if (node instanceof Element) { Element includeElement = (Element) node; //获取name属性值,封装称为一个TypedStringValue TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name")); //设置源,当前子标签 valueHolder.setSource(parserContext.extractSource(includeElement)); //加入到includePatterns中 includePatterns.add(valueHolder); } } //如果includePatterns不为空 if (!includePatterns.isEmpty()) { //设置源,当前标签 includePatterns.setSource(parserContext.extractSource(element)); //设置到自动代理创建者的bean定义的属性中 //属性名为includePatterns,值就是includePatterns集合 beanDef.getPropertyValues().add("includePatterns", includePatterns); } }

  从AnnotationAwareAspectJAutoProxyCreator中的对includePatterns属性赋值的setter方法就能明显看出来字符串到Pattern的转换:

/**
 * AnnotationAwareAspectJAutoProxyCreator的属性
 * 

* 就是此前标签解析后的模式集合,默认为null *

* 标签配置的name属性,将会转换为Pattern对象,因此它应该是一个正则表达式 * Spring会自动进行类型转换,通过Pattern.compile将字符串转换为一个Pattern模式对象 */ @Nullable private List<Pattern> includePatterns; /** 1. AnnotationAwareAspectJAutoProxyCreator的方法 2.

3. 设置正则表达式模式的列表,匹配符合条件的@AspectJ bean的名称 4. 默认值是将所有@AspectJ视为符合条件的。 5. 6. @param patterns 参数字符串集合 */ public void setIncludePatterns(List<String> patterns) { //配置为一个空集合 this.includePatterns = new ArrayList<>(patterns.size()); //遍历全部patternText字符串集合 for (String patternText : patterns) { //通过Pattern.compile,转换为正则表达式模式对象,加入到includePatterns列表中 this.includePatterns.add(Pattern.compile(patternText)); } }

2 AnnotationAwareAspectJAutoProxyCreator自动代理创建者

  AnnotationAwareAspectJAutoProxyCreator是AspectJAwareAdvisorAutoProxyCreator的子类,他继承了父类的全部特点,因此这两个自动代理创建者大量采用相同的方法以及逻辑,同时AnnotationAwareAspectJAutoProxyCreator还扩展了功能,那就是支持通过相关AspectJ注解配置AOP。
  我们重要讲解和此前讲的AspectJAwareAdvisorAutoProxyCreator不同的地方,相同的方法不会讲解!在看本文AnnotationAwareAspectJAutoProxyCreator的源码之前,应该学习AspectJAwareAdvisorAutoProxyCreator的源码。
  实际上,这两个自动代理创建者最大的不同之处就在于它们的wrapIfNecessary方法中的findCandidateAdvisors方法,AnnotationAwareAspectJAutoProxyCreator在兼容父类基于XML的AOP配置时,还新增了对于AOP注解配置的支持。
  而对于此后的createProxy创建代理对象的方法,以及AOP代理对象的方法调用拦截等方法,它们的行为逻辑则是完全一致的,因此,我们就看findCandidateAdvisors方法就行了!

2.1 findCandidateAdvisors查找候选Advisors

  该方法查找候选Advisors,子类AnnotationAwareAspectJAutoProxyCreator重写了该方法。在重写的方法中,做了两步:

  1. 首先还是调用了父类的findCandidateAdvisors方法,也即是查找基于XML配置已解析的Advisors。这是对于XML配置的兼容,此前关于AspectJAwareAdvisorAutoProxyCreator的文章中我们已经讲了这个方法,在此不再赘述。
  2. 随后从beanFactory中查找所有具有@Aspect注解的切面bean定义中的通知并构建,将切面类中全部合法的通知方法和引介字段转换为Advisor,最后返回所有的Advisor集合。这是对Aspect注解配置的支持。
/**
 * AnnotationAwareAspectJAutoProxyCreator的属性
 * 

* AspectAdvisor构建者,在BeanFactoryAware接口回调的setBeanFactory方法时被初始化 * 类型为BeanFactoryAspectJAdvisorsBuilderAdapter */ @Nullable private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder; /** * AnnotationAwareAspectJAutoProxyCreator重写的方法 *

* 查找候选Advisors */ @Override protected List<Advisor> findCandidateAdvisors() { /* * 1 调用父类的findCandidateAdvisors方法查找基于XML配置的已解析的Advisors */ List<Advisor> advisors = super.findCandidateAdvisors(); /* * 2 从beanFactory中查找所有具有@Aspect注解的切面bean定义并构建 */ if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } //将从XML配置和注解配置中找到的Advisor合并到一个集合中返回 return advisors; }

2.1.1 BeanFactoryAspectJAdvisorsBuilder构建者

  BeanFactoryAspectJAdvisorsBuilder用于帮助从beanFactory检索@Aspect的bean类,并基于它们构建 Advisor,用于自动代理。
  BeanFactoryAspectJAdvisorsBuilder 对应的aspectJAdvisorsBuilder属性是在BeanFactoryAware接口回调的setBeanFactory方法时被初始化的,类型为BeanFactoryAspectJAdvisorsBuilderAdapter。一起初始化的还有aspectJAdvisorFactory,用于构建每一个Advisor。

/**
 * AbstractAdvisorAutoProxyCreator的方法
 * 

* BeanFactoryAware接口的setBeanFactory方法实现 * 在创建自动代理创建者对象之后,自动回调 * * @param beanFactory bean工厂 */ @Override public void setBeanFactory(BeanFactory beanFactory) { super.setBeanFactory(beanFactory); if (!(beanFactory instanceof ConfigurableListableBeanFactory)) { throw new IllegalArgumentException( "AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory); } initBeanFactory((ConfigurableListableBeanFactory) beanFactory); } //------------AnnotationAwareAspectJAutoProxyCreator的相关属性------------ /** * AspectAdvisor构建者,在BeanFactoryAware接口回调的setBeanFactory方法时被初始化 * 实际类型为BeanFactoryAspectJAdvisorsBuilderAdapter */ @Nullable private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder; /** * 可从使用@AspectJ 注解语法标注的类创建 Spring AOP Advisor的工厂 * 实际类型为ReflectiveAspectJAdvisorFactory */ @Nullable private AspectJAdvisorFactory aspectJAdvisorFactory; /** * 子类AnnotationAwareAspectJAutoProxyCreator重写的方法 * * @param beanFactory bean工厂 */ @Override protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) { //调用父类的方法,这会初始化父类的advisorRetrievalHelper super.initBeanFactory(beanFactory); //创建aspectJAdvisor工厂,实际类型为ReflectiveAspectJAdvisorFactory if (this.aspectJAdvisorFactory == null) { this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory); } //创建aspectJAdvisor构建者,实际类型为BeanFactoryAspectJAdvisorsBuilderAdapter //它是AnnotationAwareAspectJAutoProxyCreator的内部类 this.aspectJAdvisorsBuilder = new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory); }

  BeanFactoryAspectJAdvisorsBuilderAdapter的构造器如下:

/**
 * BeanFactoryAspectJAdvisorsBuilderAdapter的构造器
 */
public BeanFactoryAspectJAdvisorsBuilderAdapter(
        ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
    //调用父类构造器
    super(beanFactory, advisorFactory);
}

//---------父类BeanFactoryAspectJAdvisorsBuilder的相关属性------------

private final ListableBeanFactory beanFactory;

private final AspectJAdvisorFactory advisorFactory;

/**
 * BeanFactoryAspectJAdvisorsBuilder的构造器
 *
 * @param beanFactory    要扫描的beanFactory
 * @param advisorFactory AspectjAdvisorFactory,用于构建每个顾问
 */
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
    Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
    Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
    //初始化属性
    this.beanFactory = beanFactory;
    this.advisorFactory = advisorFactory;
}

2.1.2 AspectJAdvisorFactory工厂

  AspectJAdvisorFactory对应的aspectJAdvisorFactory属性同样在BeanFactoryAware接口回调的setBeanFactory方法时被初始化。
  实际类型为ReflectiveAspectJAdvisorFactory,可以根据@AspectJ组件类内部的通知注解@Before、@Around、@After、@AfterReturning、@AfterThrowing,创建Spring的Advisor通知器,使用反射调用相应的通知方法。这些注解来自遵循AspectJ 5的注解语法的@AspectJ组件类。
Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator【一万字】_第2张图片
  继承了AbstractAspectJAdvisorFactory,该类作为抽象基类,提供了AspectJAdvisorFactory接口的基本实现。该类并不会生成Advisor,而是进行注解的解析和校验,具有的生成逻辑留给子类实现。
  AspectJAdvisorFactory接口,定义了根据AspectJ 5的通知注解创建Spring Advisor的各种方法。

/**
 * ReflectiveAspectJAdvisorFactory的属性
 * 

* 方法比较器,用于比较基于注解的通知方法并排序 */ private static final Comparator<Method> METHOD_COMPARATOR; /** * beanFactory,构造器中赋值 */ @Nullable private final BeanFactory beanFactory; /*ReflectiveAspectJAdvisorFactory的静态块,用于加载比较器*/ static { // Note: although @After is ordered before @AfterReturning and @AfterThrowing, // an @After advice method will actually be invoked after @AfterReturning and // @AfterThrowing methods due to the fact that AspectJAfterAdvice.invoke(MethodInvocation) // invokes proceed() in a `try` block and only invokes the @After advice method // in a corresponding `finally` block. //主要比较器,采用ConvertingComparator,先对传入的参数使用转换器进行转换,随后使用比较器进行比较 Comparator<Method> adviceKindComparator = new ConvertingComparator<>( //实例比较器,order值就是InstanceComparator中定义的注解的索引,如果没有这些注解就是最大值5 new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class), //实例转换器,将通知方法转换为一个AspectJAnnotation对象,该方法上标注的注解,就是对应对象的类型 (Converter<Method, Annotation>) method -> { //查找方法的注解,找到某一个注解即停止查找,然后将当前方法封装为基于该注解的AspectJAnnotation,查找注解的顺序为: //Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); return (ann != null ? ann.getAnnotation() : null); }); //次要比较器,比较方法名 Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName); METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator); } /** * ReflectiveAspectJAdvisorFactory构造器 */ public ReflectiveAspectJAdvisorFactory(@Nullable BeanFactory beanFactory) { //beanFactory赋值 this.beanFactory = beanFactory; }

2.1.3 buildAspectJAdvisors构建AspectJ通知器

  该方法在当bean工厂中查找具有@AspectJ注解的切面bean,并基于它们的配置构建对应的的Advisor,将切面类中全部合法的通知方法和引介字段转换为Advisor,最后返回所有的Advisor集合。这些都是基于AspectJ注解查找的,因此是对注解的支持!
  该方法还会使用缓存,第一进入时查找到的通知器都存入缓存集合中,后续进来时直接从缓存中获取(这要求是单例的切面bean),因为这个findCandidateAdvisors方法在AnnotationAwareAspectJAutoProxyCreator的构建过程中被多次调用,比如前面执行校验的在shouldSkip方法中。

//----------BeanFactoryAspectJAdvisorsBuilder的属性-----------

/**
 * beanFactory,构造器中初始化
 */
private final ListableBeanFactory beanFactory;

/**
 * advisorFactory,构造器中初始化
 * 实际类型为ReflectiveAspectJAdvisorFactory
 * 用于解析方法创建Advisor通知器
 */
private final AspectJAdvisorFactory advisorFactory;

/**
 * 切面的名称缓存
 */
@Nullable
private volatile List<String> aspectBeanNames;

/**
 * 通知器缓存
 */
private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>();

/**
 * 切面工厂缓存
 */
private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache = new ConcurrentHashMap<>();

/**
 1. BeanFactoryAspectJAdvisorsBuilder的方法
 2. 

3. 在当bean工厂中查找具有@AspectJ注解的切面bean,并基于它们的配置构建对应的的Advisor 4. 将切面类中全部合法的通知方法和引介字段转换为Advisor通知器集合 5. 最后返回所有构建的Advisor集合 */ public List<Advisor> buildAspectJAdvisors() { //获取缓存的切面名称集合 List<String> aspectNames = this.aspectBeanNames; //如果集合为null,表示第一次调用该方法 if (aspectNames == null) { //加锁,保证同步 synchronized (this) { aspectNames = this.aspectBeanNames; //如果集合为null if (aspectNames == null) { //该集合用于保存构建的Advisor List<Advisor> advisors = new ArrayList<>(); //初始化aspectNames集合 aspectNames = new ArrayList<>(); /* * 获取全部Object类型的bean定义的beanName数组,注意是Object类型 * 这个方法我们在"IoC容器初始化(6)"的文章中已经讲过了 */ String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); /*遍历beanName数组,查找所有切面类中的头通知器*/ for (String beanName : beanNames) { /* * 判断切面bean的名称是否合格,也就是符合子标签的配置模式 * * 该方法在BeanFactoryAspectJAdvisorsBuilder中默认返回true, * 被子类BeanFactoryAspectJAdvisorsBuilderAdapter重写,重写的方法中会 * 调用AnnotationAwareAspectJAutoProxyCreator的isEligibleAspectBean继续判断 */ if (!isEligibleBean(beanName)) { //如果不合格,直接跳过,判断下一个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. //获取当前beanName对应的bean定义的所属类的类型 Class<?> beanType = this.beanFactory.getType(beanName); //如果类型为null,直接跳过,判断下一个beanName if (beanType == null) { continue; } /* * 判断是否是切面类,即判断当前类以及它继承的超类或者实现的接口上是否具有@Aspect注解 * 只有具有@Aspect注解的类才能被进一步处理,也就是解析为Advisor * * 从这里能够看出来,bean定义只有先被注册到容器中,才能进一步解析@Aspect注解 * 因此对于切面类我们通常需要同时添加@Component和@Aspect注解 */ if (this.advisorFactory.isAspect(beanType)) { //当前beanName加入到aspectNames缓存中,后续从缓存中直接获取 aspectNames.add(beanName); //根据当前beanType和beanName,新建一个AspectMetadata切面元数据 //保存了@Aspect注解的信息以及当前切面类的信息 AspectMetadata amd = new AspectMetadata(beanType, beanName); /* * 如果没有设置@Aspect注解的value属性值,那么就是默认就是单例的切面类 * value可以设置为值有perthis()、pertarget()、percflow()等等,可以设置切面类的作用域 */ if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { /*新建一个BeanFactoryAspectInstanceFactory工厂对象,用于创建AspectJ 切面实例*/ MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); /* * 通过advisorFactory调用getAdvisors方法 * 将当前切面类中全部合法的通知方法和引介字段转换为Advisor通知器集合 */ List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); //如果当前切面类是单例bean if (this.beanFactory.isSingleton(beanName)) { //将当前切面类beanName和内部的通知器集合存入advisorsCache缓存 this.advisorsCache.put(beanName, classAdvisors); } else { //否则,将当前切面类beanName和切面实例工厂存入aspectFactoryCache缓存 this.aspectFactoryCache.put(beanName, factory); } //当前切面类的所有通知器加入到advisors总集合中 advisors.addAll(classAdvisors); } //对于其他切面类的作用域的处理 else { // Per target or per this. //如果当前切面bean是单例的,那么抛出异常 if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton"); } /*新建一个PrototypeAspectInstanceFactory工厂对象,用于创建AspectJ 切面实例,可能会多次实例化*/ MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); //将当前切面类beanName和切面实例工厂存入aspectFactoryCache缓存 this.aspectFactoryCache.put(beanName, factory); //当前切面类的所有通知器加入到advisors总集合中 advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } //aspectNames赋给aspectBeanNames缓存起来,后续从缓存中直接获取 this.aspectBeanNames = aspectNames; //返回通知器集合 return advisors; } } } //如果切面名集合为空,那么返回一个空list if (aspectNames.isEmpty()) { return Collections.emptyList(); } List<Advisor> advisors = new ArrayList<>(); /* * 遍历全部切面名集合, */ for (String aspectName : aspectNames) { //从advisorsCache缓存中根据切面名获取该切面的通知器集合 List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); //如果不为null,说明已缓存,那么直接获取 if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { //如果为null,说明这个切面bean不是单例bean,那么从新获取 MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } //返回通知器集合 return advisors; }

2.1.3.1 isEligibleAspectBean是否是合格的切面bean

  isEligibleAspectBean方法用于判断当前bean的是否是合格的切面bean。 其逻辑就是:

  1. 如果没有配置< aop:include/>标签,则所有 bean都是合格的切面bean。一般都不会配置< aop:include/>标签。
  2. 如果配置了< aop:include/>标签,即"包含模式" includePatterns非null,则当前bean的beanName必须匹配其中一个模式必须才能算合格,否则算不合格。
/**
 * BeanFactoryAspectJAdvisorsBuilder的方法
 * 

* 确定具有给定名称的切面 bean 是否合格,默认实现返回true */ protected boolean isEligibleBean(String beanName) { return true; } /** * BeanFactoryAspectJAdvisorsBuilderAdapter重写的方法 */ @Override protected boolean isEligibleBean(String beanName) { //调用AnnotationAwareAspectJAutoProxyCreator对象的isEligibleAspectBean方法 return AnnotationAwareAspectJAutoProxyCreator.this.isEligibleAspectBean(beanName); } /** * AnnotationAwareAspectJAutoProxyCreator的属性 *

* 就是此前标签解析后的模式集合,默认为null *

* 标签配置的name属性,将会转换为Pattern对象,因此它应该是一个正则表达式 * Spring会自动进行类型转换,通过Pattern.compile将字符串转换为一个Pattern模式对象 */ @Nullable private List<Pattern> includePatterns; /** * AnnotationAwareAspectJAutoProxyCreator重写的方法 *

* 检查给定切面 bean 是否符合自动代理条件。 *

* 如果没有配置标签,则"包含模式"将为null,并且包括所有 bean。 * 如果"包含模式"为非null,则其中一个模式必须匹配。 * * @param beanName 给定的beanName */ protected boolean isEligibleAspectBean(String beanName) { //如果没有设置包含模式,即没有标签,那么默认返回true,表示全部符合条件 if (this.includePatterns == null) { return true; } //如果设置了包含模式,那么给定的beanName至少匹配一个模式 else { for (Pattern pattern : this.includePatterns) { //如果给定的beanName至少匹配一个模式(正则表达式),就直接返回true if (pattern.matcher(beanName).matches()) { return true; } } //都不匹配,那么返回false return false; } }

2.1.3.2 isAspect是否是切面bean

  通过判断bean定义所属的类,以及它继承的超类或者实现的接口上是否具有@Aspect注解,来判断当前bean定义是否是Aspect切面组件类。
  实际上还会排除被AspectJ编译器(AJC)编译的类,当然默认都是采用javac编译器编译的。

/**
 * AbstractAspectJAdvisorFactory的方法
 * 

* 是否是Aspect切面组件类,即类上是否标注了@Aspect注解 */ @Override public boolean isAspect(Class<?> clazz) { //如果类上具有@Aspect注解,并且不是通过AspectJ编译器(ajc)编译的源码 return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz)); } /** * AbstractAspectJAdvisorFactory的方法 *

* 类上是否具有@Aspect注解 */ private boolean hasAspectAnnotation(Class<?> clazz) { return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null); } /** * AbstractAspectJAdvisorFactory的属性 *

* AJC编译器的编译后的字段特征,将会加上"ajc$"前缀 */ private static final String AJC_MAGIC = "ajc$"; /** * AbstractAspectJAdvisorFactory的方法 *

* 判断是否采用AJC编译器 * 默认情况下,IntelliJ IDEA 使用 javac 编译器,我们可以手动设置使用AJC编译器 */ private boolean compiledByAjc(Class<?> clazz) { //通过判断字段名是否被修改为"ajc$",来判断是否使用了AspectJ编译器(AJC) for (Field field : clazz.getDeclaredFields()) { //如果有一个字段名以"ajc$"为前缀,那么就算采用AJC编译器 if (field.getName().startsWith(AJC_MAGIC)) { return true; } } return false; }

2.1.3.3 BeanFactoryAspectInstanceFactory切面实例工厂

  BeanFactoryAspectInstanceFactory作为切面实例工厂,用于获取切面类的实例!

/**
 * BeanFactoryAspectInstanceFactory的构造器
 *
 * @param beanFactory beanFactory
 * @param name        beanName
 */
public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) {
    //调用另一个构造器
    this(beanFactory, name, null);
}

//---------BeanFactoryAspectInstanceFactory的相关属性

private final BeanFactory beanFactory;

private final String name;

private final AspectMetadata aspectMetadata;

/**
 * BeanFactoryAspectInstanceFactory的构造器
 */
public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name, @Nullable Class<?> type) {
    Assert.notNull(beanFactory, "BeanFactory must not be null");
    Assert.notNull(name, "Bean name must not be null");
    //保存属性
    this.beanFactory = beanFactory;
    this.name = name;
    Class<?> resolvedType = type;
    if (type == null) {
        //获取bean定义的类型
        resolvedType = beanFactory.getType(name);
        Assert.notNull(resolvedType, "Unresolvable bean type - explicitly specify the aspect class");
    }
    //保存一个AspectMetadata
    this.aspectMetadata = new AspectMetadata(resolvedType, name);
}

2.1.3.4 getAdvisors获取Advisors通知器

  为指定切面类上的所有具有通知注解的方法和具有引介注解的字段生成Advisor,通知方法生成InstantiationModelAwarePointcutAdvisorImpl类型的切入点通知器,引介字段生成DeclareParentsAdvisor类型的引介通知器,最后返回找到的全部通知器集合。

/**
 * ReflectiveAspectJAdvisorFactory的方法
 * 

* 为指定切面类上的所有具有通知注解的方法和具有引介注解的字段生成Advisor * 返回找到的全部通知器集合 * * @param aspectInstanceFactory 切面实例工厂 * @return 解析的Advisor通知器集合 */ @Override public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { //获取切面类的class Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); //获取切面类的name,就是beanName String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); //校验切面类,后面还会校验几次 validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator // so that it will only instantiate once. //使用装饰器包装当前的aspectInstanceFactory对象,使得内部的切面类实例只被创建一次 //因为aspectInstanceFactory创建的切面类实例将被缓存起来 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); //找到的通知器集合 List<Advisor> advisors = new ArrayList<>(); /* * getAdvisorMethods获取当前切面类中的全部方法 * 排除桥接方法、合成方法、具有@Pointcut注解的方法,所以说普通方法也会被加进来 * 因此还需要继续筛选和处理 */ for (Method method : getAdvisorMethods(aspectClass)) { // Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect // to getAdvisor(...) to represent the "current position" in the declared methods list. // However, since Java 7 the "current position" is not valid since the JDK no longer // returns declared methods in the order in which they are declared in the source code. // Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods // discovered via reflection in order to support reliable advice ordering across JVM launches. // Specifically, a value of 0 aligns with the default value used in // AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor). //处理方法,尝试转换为Advisor通知器 //Spring 5.2.7之前,advisors.size()作为第三个参数,以便确定位置。但是Java7开始,JDK不再按在源代码中声明的方法的顺序返回声明的方法 //因此,对于通过反射发现的所有通知方法,我们现在将第三个参数declarationOrderInAspect通过硬编码设置为0,所有的通知的declarationOrder都是0 //返回的通知器实际类型为InstantiationModelAwarePointcutAdvisorImpl,属于PointcutAdvisor切入点通知器 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { //切入点通知器加入通知器集合 advisors.add(advisor); } } // If it's a per target aspect, emit the dummy instantiating aspect. //如果通知器集合不为空,并且属于延迟初始化的切面类,那么在通知器列表头部加入一个SyntheticInstantiationAdvisor同步实例通知器 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // Find introduction fields. /* * 查找并解析引介增强字段,即@DeclareParents注解 */ //遍历全部字段 for (Field field : aspectClass.getDeclaredFields()) { //从该字段获取引介增强的通知器 //返回的通知器实际类型为DeclareParentsAdvisor,属于IntroductionAdvisor引介通知器 Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { //引介通知器加入通知器集合 advisors.add(advisor); } } //返回通知器集合 return advisors; }

2.1.3.4.1 validate校验切面类

  校验切面类,该方法后面还会执行多次。

/**
 * AbstractAspectJAdvisorFactory的方法
 * 

* 校验切面类 * * @param aspectClass 切面类的类型 */ @Override public void validate(Class<?> aspectClass) throws AopConfigException { // 如果父类具有@Aspect注解且不是抽象的,则抛出异常:"cannot extend concrete aspect" if (aspectClass.getSuperclass().getAnnotation(Aspect.class) != null && !Modifier.isAbstract(aspectClass.getSuperclass().getModifiers())) { throw new AopConfigException("[" + aspectClass.getName() + "] cannot extend concrete aspect [" + aspectClass.getSuperclass().getName() + "]"); } /* * 获取当前切面类的AjType,即返回给定 Java 类型的 AspectJ 运行时类型表示形式 * AjType是AspectJ程序中切面类的运行时表示形式,区别于 java.lang.class, * 可以获取从中获取理解切入点、通知、declare和其他 AspectJ 类型的成员 */ AjType<?> ajType = AjTypeSystem.getAjType(aspectClass); //如果不是切面类,抛出异常 if (!ajType.isAspect()) { throw new NotAnAtAspectException(aspectClass); } //如果切面类的生命周期,即@Aspect注解的value属性值被设置为"percflow()",那么抛出异常 if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOW) { throw new AopConfigException(aspectClass.getName() + " uses percflow instantiation model: " + "This is not supported in Spring AOP."); } //如果切面类的生命周期,即@Aspect注解的value属性值被设置为"percflowbelow()",那么抛出异常 if (ajType.getPerClause().getKind() == PerClauseKind.PERCFLOWBELOW) { throw new AopConfigException(aspectClass.getName() + " uses percflowbelow instantiation model: " + "This is not supported in Spring AOP."); } }

2.1.3.4.2 getAdvisorMethods获取全部通知方法

  获取当前切面类中的全部通知方法并排序,排除桥接方法、合成方法、具有@Pointcut注解的方法,所以说普通方法也会被加进来。

/**
 * ReflectionUtils的属性
 * 

* 通知方法筛选过滤器 *

* 排除桥接方法。桥接方法是编译器生成的方法,实际上这就是"泛型擦除"过程中所做的事之一,用于兼容JDK 1.5 引入的泛型 * 排除合成方法。由编译器生成的方法就是合成方法,通常可用于辅助外部类和内部类的相互访问,比如私有属性访问。桥接方法也属于合成方法 */ public static final ReflectionUtils.MethodFilter USER_DECLARED_METHODS = (method -> !method.isBridge() && !method.isSynthetic()); /** * ReflectiveAspectJAdvisorFactory的属性 *

* 方法比较器,用于比较基于注解的通知方法并排序 * 在加载ReflectiveAspectJAdvisorFactory类的时候就被初始化了 */ private static final Comparator<Method> METHOD_COMPARATOR; /** * ReflectiveAspectJAdvisorFactory的方法 *

* 获取当前切面类中的全部通知方法 * 排除桥接方法、合成方法、具有@Pointcut注解的方法,所以说普通方法也会被加进来 * * @param aspectClass 切面类类型 * @return 当前切面类中的全部通知方法 */ private List<Method> getAdvisorMethods(Class<?> aspectClass) { final List<Method> methods = new ArrayList<>(); /* * 循环过滤所有的方法(不包括构造器) * 第一个参数:要查找的类 * 第二个参数:方法回调 * 第三个参数:方法过滤器,这里是USER_DECLARED_METHODS,即排除桥接方法和合成方法 */ ReflectionUtils.doWithMethods(aspectClass, method -> { // Exclude pointcuts //如果当前方法不是桥接方法和合成方法,并且没有@Pointcut注解 //那么算作通知方法,所以说普通方法也会被加进来 if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) { methods.add(method); } }, ReflectionUtils.USER_DECLARED_METHODS); //通过比较器,排序 if (methods.size() > 1) { methods.sort(METHOD_COMPARATOR); } return methods; }

2.1.3.4.3 getAdvisor根据通知方法尝试获取通知器

  上面的getAdvisorMethods方法仅仅是对切面类的方法进行了部分过滤,其中还包含了普通的方法,因此这里的getAdvisor方法还需要进一步过滤,只有具有通知注解的方法才能算是通知方法,随后会将该方法封装成一个InstantiationModelAwarePointcutAdvisorImpl类型的通知器返回。

/**
 * ReflectiveAspectJAdvisorFactory的方法
 * 

* 根据给定方法尝试转换为通知器 * * @param candidateAdviceMethod 候选通知方法 * @param aspectInstanceFactory 切面类实例工厂 * @param declarationOrderInAspect 生命顺序,目前Spring 5.2.8版本都是固定0 * @param aspectName 切面名,就是beanName * @return 如果该方法不是 AspectJ 通知方法,则返回null */ @Override @Nullable public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { //再次校验 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); /* * 获取当前通知方法对应的切入点实例,封装了当前通知的切入点表达式的信息 */ AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); //如果没有切入点表达式,那么直接返回null,对于普通方法,将在这里返回null if (expressionPointcut == null) { return null; } /* * 新建一个InstantiationModelAwarePointcutAdvisorImpl类型的通知器返回 */ return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }

2.1.3.4.3.1 getPointcut获取切入点表达式对象

  获取当前通知方法对应的切入点表达式对象,即AspectJExpressionPointcut对象,内部的切入点表达式一般都是通知注解的pointcut或者value属性的值。如果是一个普通方法,那么将获取一个null,进而该方法被丢弃。
  一个通知方法上理应只有一个通知注解,如果有多个注解,那么只会有一个生效,只会获取该注解的切入点表达式对象。查找顺序为:Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class。

/**
 * ReflectiveAspectJAdvisorFactory的方法
 *
 * 获取当前通知方法对应的切入点表达式对象
 *
 * @param candidateAdviceMethod 候选通知方法
 * @param candidateAspectClass  候选切面类类型
 * @return AspectJExpressionPointcut
 */
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    //查找当前方法上的通知,获取AspectJAnnotation对象
    //这里的查找是按顺序的短路查找,顺序为:Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
    //如果找到一个通知注解,就立马封装后返回,如果有其他通知注解则被丢弃
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    //如果没有通知注解,则返回null
    if (aspectJAnnotation == null) {
        return null;
    }
    //新建一个AspectJExpressionPointcut实例
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    //设置expression表达式的值,也就是上面的通知注解的pointcut或者value属性的值
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    //设置beanFactory信息
    if (this.beanFactory != null) {
        ajexp.setBeanFactory(this.beanFactory);
    }
    return ajexp;
}
2.1.3.4.3.1.1 findAspectJAnnotationOnMethod查找AspectJ 注解

  查找并返回给定方法上的第一个 AspectJ 注解,一个方法上理应只有一个AspectJ注解。
  这里的查找是按顺序的短路查找,顺序为:Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing。如果找到一个通知注解,就立马封装后返回,如果有其他注解则被丢弃。

/**
 * AbstractAspectJAdvisorFactory的属性
 * 

* AspectJ注解类型数组 */ private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[]{ Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}; /** * AbstractAspectJAdvisorFactory的方法 *

* 查找并返回给定方法上的第一个 AspectJ 注解,方法上理应只有一个AspectJ注解 */ @SuppressWarnings("unchecked") @Nullable protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) { //顺序遍历查找:Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) { AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz); //找到之后就返回 if (foundAnnotation != null) { return foundAnnotation; } } return null; } /** * AbstractAspectJAdvisorFactory的方法 * * 在给定方法上查找给定类型的AspectJ 注解 * * @param method 通知方法 * @param toLookFor 需要查找的AspectJ 注解类型 * @return 结果,没找到就返回null */ @Nullable private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) { //通用查找注解的方法,找到之后返回该注解,找不到就返回null A result = AnnotationUtils.findAnnotation(method, toLookFor); if (result != null) { //找到之后封装为一个AspectJAnnotation return new AspectJAnnotation<>(result); } else { return null; } }

2.1.3.4.3.2 InstantiationModelAwarePointcutAdvisorImpl通知器

  一个通知方法最终被解析为一个InstantiationModelAwarePointcutAdvisorImpl类型的Advisor通知器,内部封装了很多属性。

//---------InstantiationModelAwarePointcutAdvisorImpl的相关属性----------

/**
 * 切入点
 */
private final AspectJExpressionPointcut declaredPointcut;

/**
 * 切面类class
 */
private final Class<?> declaringClass;

/**
 * 通知方法名
 */
private final String methodName;

/**
 * 参数类型数组
 */
private final Class<?>[] parameterTypes;

/**
 * 通知方法
 */
private transient Method aspectJAdviceMethod;

/**
 * 当前ReflectiveAspectJAdvisorFactory工厂对象
 */
private final AspectJAdvisorFactory aspectJAdvisorFactory;

/**
 * 切面类实例工厂,用于获取切面类实例单例
 */
private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;

/**
 * 声明的顺序,目前Spring 5.2.8版本都是固定0
 */
private final int declarationOrder;

/**
 * 切面名,就是beanName
 */
private final String aspectName;

/**
 * 切入点
 */
private final Pointcut pointcut;

/**
 * 是否配置了懒加载,默认false
 */
private final boolean lazy;

/**
 * 通知
 */
@Nullable
private Advice instantiatedAdvice;

/**
 1. @param declaredPointcut      切入点
 2. @param aspectJAdviceMethod   通知方法
 3. @param aspectJAdvisorFactory 当前ReflectiveAspectJAdvisorFactory工厂对象
 4. @param aspectInstanceFactory 切面类实例工厂,用于获取切面类实例单例
 5. @param declarationOrder      声明的顺序,目前Spring 5.2.8版本都是固定0
 6. @param aspectName            切面名,就是beanName
 */
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
                                                  Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
                                                  MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    this.declaredPointcut = declaredPointcut;
    this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    this.methodName = aspectJAdviceMethod.getName();
    this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    this.aspectJAdviceMethod = aspectJAdviceMethod;
    this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    this.aspectInstanceFactory = aspectInstanceFactory;
    this.declarationOrder = declarationOrder;
    this.aspectName = aspectName;
    //切面是否配置了懒加载,一般没有
    if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        // Static part of the pointcut is a lazy type.
        Pointcut preInstantiationPointcut = Pointcuts.union(
                aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

        // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
        // If it's not a dynamic pointcut, it may be optimized out
        // by the Spring AOP infrastructure after the first evaluation.
        this.pointcut = new PerTargetInstantiationModelPointcut(
                this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
        this.lazy = true;
    }
    //这是大部分切面类的配置,即单例
    else {
        // A singleton aspect.

        this.pointcut = this.declaredPointcut;
        this.lazy = false;
        //初始化通知,还记得基于XML的配置吗,对应的通知是通过bean定义的形式自动初始化的
        this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
    }
}
2.1.3.4.3.2.1 instantiateAdvice实例化Advice通知

  实例化该通知器对应的Advice通知,通知实例用于执行通知方法的回调或者适配成拦截器。还记得基于XML的配置吗,对应的通知是在解析XML标签的通过构建bean定义的形式让Spring帮我们自动初始化的,在基于注解的这里,就是手动new的形式!

  1. 如果是@Before通知,那么新建AspectJMethodBeforeAdvice类型的通知;
  2. 如果是@After通知,那么新建AspectJAfterAdvice类型的通知;
  3. 如果是@AfterReturning通知,那么新建AspectJAfterReturningAdvice类型的通知;
  4. 如果是@AfterThrowing通知,那么新建AspectJAfterReturningAdvice类型的通知;
  5. 如果是@Around通知,那么新建AspectJAroundAdvice类型的通知;
/**
 * InstantiationModelAwarePointcutAdvisorImpl的属性
 * 空白通知
 */
private static final Advice EMPTY_ADVICE = new Advice() {};

/**
 * 实例化该通知器对应的通知
 *
 * @param pointcut 切入点
 * @return 通知实例
 */
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {

    Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
            this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    return (advice != null ? advice : EMPTY_ADVICE);
}


/**
 * ReflectiveAspectJAdvisorFactory的内部枚举
 * 

* AspectJ 注解类型的枚举 */ protected enum AspectJAnnotationType { AtPointcut, AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing } /** * AbstractAspectJAdvisorFactory的属性 * 参数名称发现器 */ protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer(); /** * ReflectiveAspectJAdvisorFactory的方法 *

* 为给定的AspectJ 通知方法构建一个Advice通知实例 *

* 通知实例用于执行通知方法的回调或者适配成拦截器 * * @param candidateAdviceMethod 通知方法 * @param expressionPointcut 切入点 * @param aspectInstanceFactory 切面类实例工厂,用于获取切面类实例单例 * @param declarationOrder 声明的顺序,目前Spring 5.2.8版本都是固定0 * @param aspectName 切面名,就是beanName * @return 一个Advice */ @Override @Nullable public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { //获取切面类类型 Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); //校验切面类 validate(candidateAspectClass); //查找一个AspectJ 注解 AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); //如果没找到就返回null if (aspectJAnnotation == null) { return null; } // If we get here, we know we have an AspectJ method. // Check that it's an AspectJ-annotated class //如果当前类不是被@AspectJ注解标注的切面类,那么抛出异常 if (!isAspect(candidateAspectClass)) { throw new AopConfigException("Advice must be declared inside an aspect type: " + "Offending method '" + candidateAdviceMethod + "' in class [" + candidateAspectClass.getName() + "]"); } if (logger.isDebugEnabled()) { logger.debug("Found AspectJ method: " + candidateAdviceMethod); } /* * 获取AspectJ注解的类型,匹配枚举类型并创建对应类型的通知,一共有六种 */ AbstractAspectJAdvice springAdvice; switch (aspectJAnnotation.getAnnotationType()) { //如果是切入点注解,即@Pointcut case AtPointcut: if (logger.isDebugEnabled()) { logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'"); } //那么直接返回null,因为这里需要的是通知注解 return null; //如果是环绕通知注解,即@Around case AtAround: //那么新建一个AspectJAroundAdvice类型的通知 springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; //如果是前置通知注解,即@Before case AtBefore: //那么新建一个AspectJMethodBeforeAdvice类型的通知 springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; //如果是最终通知注解,即@After case AtAfter: //那么新建一个AspectJAfterAdvice类型的通知 springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; //如果是后置通知注解,即@AfterReturning case AtAfterReturning: //那么新建一个AspectJAfterReturningAdvice类型的通知 springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); //获取@AfterReturning注解 AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); //如果注解设置了returning属性,表示需要传递方法返回值参数,那么设置后置通知的returningName属性 if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; //如果是异常通知注解,即@AfterThrowing case AtAfterThrowing: //那么新建一个AspectJAfterThrowingAdvice类型的通知 springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); //获取@AfterThrowing注解 AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); //如果注解设置了throwing属性,表示需要传递方法异常参数,那么设置异常通知的throwingName属性 if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; //其他情况,抛出异常 default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } /* * 配置通知 */ // Now to configure the advice... //设置aspectName,即切面名 springAdvice.setAspectName(aspectName); //设置declarationOrder,默认都是0 springAdvice.setDeclarationOrder(declarationOrder); //通过参数名称发现器获取传递的参数 //实际上就是获取通知注解上的argNames属性值,并且根据","进行拆分 String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { //设置给argumentNames属性,并且可能会补充第一个参数名(如果第一个参数是JoinPoint或者ProceedingJoinPoint或者JoinPoint.StaticPart) springAdvice.setArgumentNamesFromStringArray(argNames); } //辅助参数绑定,后面执行invoke拦截器的时候就不会再绑定了 springAdvice.calculateArgumentBindings(); //返回通知 return springAdvice; }

2.1.3.4.4 getDeclareParentsAdvisor获取引介增强通知器

  在解析引介增强字段时,调用该方法根据给定的字段创建一个DeclareParentsAdvisor引介增强通知器,如果不是引介增强字段则返回null。

/**
 * ReflectiveAspectJAdvisorFactory的方法
 * 

* 根据给定的字段创建一个DeclareParentsAdvisor引介增强通知器 * * @param introductionField 字段 * @return 如果不是引介增强字段则返回null */ @Nullable private Advisor getDeclareParentsAdvisor(Field introductionField) { //获取字段上的@DeclareParents注解 DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class); //如果没有@DeclareParents注解,说明是普通字段,返回null if (declareParents == null) { // Not an introduction field return null; } //如果注解的defaultImpl属性值为默认值(默认值就是DeclareParents.class) //那么抛出异常:"'defaultImpl' attribute must be set on DeclareParents" if (DeclareParents.class == declareParents.defaultImpl()) { throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents"); } //解析@DeclareParents注解,返回一个新建的DeclareParentsAdvisor通知器 return new DeclareParentsAdvisor( introductionField.getType(), declareParents.value(), declareParents.defaultImpl()); }

3 @EnableAspectJAutoProxy注解解析

  基于注解的Spring AOP配置需要通过XML文件的< aop:aspectj-autoproxy/>标签开启,这看起来确实挺滑稽的。因此,Spring还为我们提供了一个@EnableAspectJAutoProxy 注解,用来代替< aop:aspectj-autoproxy/>标签,我们学习了这个注解之后,就可以真正的和XML文件说拜拜了!而这也是目前Spring AOP的主流配置,实际上目前Spring的各种组件都在尝试丢弃XML文件,采用基于注解和Java配置的形式!
  @EnableAspectJAutoProxy通常标注在配置类上,这样在容器启动的时候就能解析这个注解。

3.1 @EnableAspectJAutoProxy注解的源码

  我们先看看@EnableAspectJAutoProxy注解的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//核心元注解,通过@Import注解注入一个AspectJAutoProxyRegistrar的bean定义
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    /**
     * 指示是否强制采用CGLIB创建代理对象
     * 

* 默认值为false,表示首先尝试采用基于 Java 接口的代理,不行再采用CGLIB的代理 * 手动设置为true,表示强制创建基于子类的代理,即采用CGLIB代理 */ boolean proxyTargetClass() default false; /** * 用于暴露代理对象,主要用来解决同一个目标类的方法互相调用时代理不生效的问题 *

* 默认值为false表示不开启 * 设置为true表示开启,那么就可以在被代理方法中通过AopContext.currentProxy()获取当前代理类对象 */ boolean exposeProxy() default false; }

  可以看到,除了常见的两个属性之外,还有一个非常重要的注解,就是@Import(AspectJAutoProxyRegistrar.class) ,关于该注解,我们在此前的“ConfigurationClassPostProcessor配置类后处理器”的文章中就说过这个注解,它就是在ConfigurationClassPostProcessor配置类后处理器中被解析的,它的作用同样是引入bean定义。直觉告诉我们,这个@Import(AspectJAutoProxyRegistrar.class)注解中的AspectJAutoProxyRegistrar就是@EnableAspectJAutoProxy注解起作用的关键类!

3.2 AspectJAutoProxyRegistrar自动代理注册员

  从该类的名字也能看出来,它就是用于注册自动代理创建者的!这个类的uml类图如下:
Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator【一万字】_第3张图片
  很简单,就是实现了ImportBeanDefinitionRegistrar接口,关于这个接口,我们也在“ConfigurationClassPostProcessor配置类后处理器”的文章中讲解过了:实现了ImportBeanDefinitionRegistrar接口的class,该class对应的类本身不会被注册为bean定义,但是它的registerBeanDefinitions方法可用于自定义注册bean定义,并且该方法会被在处理过程中自动调用。

  好了,现在我们明白了,使用@Import(AspectJAutoProxyRegistrar.class)注解的目的并不是注册这个AspectJAutoProxyRegistrar类的bean定义(它本身不会被注册),而是调用它内部的registerBeanDefinitions方法,那么我们就来看看这个方法!

/**
 * AspectJAutoProxyRegistrar的方法
 * 

* 注册、配置一个AnnotationAwareAspectJAutoProxyCreator类型的自动代理创建者 */ @Override public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { /* * 1 尝试注册或者升级一个名为"org.springframework.aop.config.internalAutoProxyCreator" * 类型为AnnotationAwareAspectJAutoProxyCreator的自动代理创建者的bean定义 * * 这个方法,我们在解析标签的源码中就见过了,这就是核心逻辑 */ AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); /* * 2 解析@EnableAspectJAutoProxy注解的属性,配置自动代理创建者 */ //获取@EnableAspectJAutoProxy注解的属性集合 AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAspectJAutoProxy != null) { //如果设置了proxyTargetClass属性为true if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) { //配置自动代理创建者强制使用CGLIB代理 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } //如果设置了exposeProxy属性为true if (enableAspectJAutoProxy.getBoolean("exposeProxy")) { //配置自动代理创建者强制暴露代理对象 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } }

  可以看到,该方法的目的就是注册、配置一个AnnotationAwareAspectJAutoProxyCreator类型的自动代理创建者。它内部调用的方法,我们在之前的文章中都见过了。所以说还是比较简单的。
  基于@EnableAspectJAutoProxy 注解配置Spring AOP,最重要的就是它上面的@Import(AspectJAutoProxyRegistrar.class注解,为此,我们必须了解@Import注解的含义,由于引入的AspectJAutoProxyRegistrar类属于ImportBeanDefinitionRegistrar接口的实现,因此它的registerBeanDefinitions方法将会在ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry扩展回调方法中被调用,从而实现自动注册、配置一个AnnotationAwareAspectJAutoProxyCreator类型的自动代理创建者的目的。从这方面也能看出来,Spring框架的可扩展性非常好,实际上很多注解的解析都是在Spring提供的扩展点方法中进行的,这一点,可以去看看此前我们讲的“IoC容器初始化”的系列文章就能直观的感受到!

4 补充:Spring Boot 的自动配置

  本来这里的内容应该在Spring Boo部分再讲解的,但是这里加上,并没有讲源码,只要讲效果!
  前面我们所讲的Spring AOP都是单纯使用Sping的项目,如果是基于Spring Boot的项目,那么Spring Boot可能会为我们自动配置一个自动代理创建者(这不是一定的),并且默认采用的是基于类的CGLIB代理,没想到吧,因为Spring开发人员认为CGLIB代理在Spring Boot这种比较大型的框架中性能更强!
  自动配置的源码就在AopAutoConfiguration自动配置类中(Spring Boot的自动配置原理就是靠这些自动配置类实现的)。因此,在某些情况下,我们连@EnableAspectJAutoProxy注解都不必添加了。
  由于Spring Boot源码随着版本变动很大,因此这里仅仅贴出两个Spring Boot版本的AopAutoConfiguration源码,不代表最新版本!

  Spring Boot 2.1.7.RELEASE:

  1. 如果该项目引入了spring-context的依赖包(具有@EnableAspectJAutoProxy类),并且引入了aspectjweaver的依赖包(具有@Aspect、Advice、AnnotatedElement这些类),并且配置的spring.aop.auto属性为true(默认为true),那么将会自动配置一个@EnableAspectJAutoProxy注解,即注册一个AnnotationAwareAspectJAutoProxyCreator类型的自动代理创建者。
    1. 根据配置的spring.aop.proxy-target-class属性值选择代理方式(默认为true),如果为true,那么添加一个@EnableAspectJAutoProxy(proxyTargetClass = true)注解,即强制CGLIB代理,否则添加一个@EnableAspectJAutoProxy(proxyTargetClass = false),即优先JDK代理。
      Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator【一万字】_第4张图片
        Spring Boot 2.3.0.RELEASE:
  2. 如果该项目配置的spring.aop.auto属性为true(默认为true),那么将尝试自动配置代理:
    1. 如果该项目引入了aspectjweaver的依赖包(具有Advice类),那么将会自动配置一个@EnableAspectJAutoProxy注解,即注册一个AnnotationAwareAspectJAutoProxyCreator类型的自动代理创建者。
      1. 根据配置的spring.aop.proxy-target-class属性值选择代理方式(默认为true),如果为true,那么添加一个@EnableAspectJAutoProxy(proxyTargetClass = true)注解,即强制CGLIB代理,否则添加一个@EnableAspectJAutoProxy(proxyTargetClass = false),即优先JDK代理。
    2. 如果该项目没有引入aspectjweaver的依赖包(没有Advice类),并且spring.aop.proxy-target-class属性值为true(默认为true),那么将注册一个InfrastructureAdvisorAutoProxyCreator类型的自动代理创建者,并且强制CGLIB代理。
      Spring AOP源码(4)—基于注解的AOP源码解析以及AnnotationAwareAspectJAutoProxyCreator【一万字】_第5张图片

5 总结

  本文我们学习了基于注解的Sping AOP流程,这一切都是在前几篇文章之上进行讲解的,包括对于< aop:aspectj-autoproxy/>AOP标签的解析、对于切面注解的解析、对于@EnableAspectJAutoProxy注解的解析以及Sping Boot中Spring AOP的自动配置原理!
   到目前为止,这四篇文章AOP中,一步步的讲解了:XML的AOP配置的原理、代理对象的创建、代理方法的调用、注解的AOP配置的原理这几个重要的过程。实际上,通用Spring AOP的源码流程已经讲解完毕。后面有时间我们会介绍特别的Spring AOP源码,比如声明式事物的支持,很明显Spring的声明式事务机制也是通过AOP实现的!另外,比如@Configuration配置类、@Async异步任务机制、@EventListener事件发布机制等等都是通过AOP实现的,只不过这些实现都不是走的上面讲的通用的实现方式,但是大体原理都差不多,后面有时间再一一讲解!

相关文章:
  https://spring.io/
  Spring 5.x 学习
  Spring 5.x 源码

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

你可能感兴趣的:(Spring,5.x,源码,Spring,AOP注解原理,EnableAspectJA,aspectj-autopr)