Spring 源码阅读 53:查找注解配置的切面增强逻辑(3)- 创建 Advisor

简介: 本文分析了 Spring 查找注解配置的 AOP 切面信息的最后一部分,Spring 会将查找到的所有增强类中的方法进行筛选,找到所有的增强方法,并将其封装成对应的 Advisor。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 52:查找注解配置的切面增强逻辑(2)- 查找增强方法

概述

查找基于注解的切面配置增强逻辑的源码分析,总共分三篇,这是第三篇,强烈建议按顺序阅读。

前面我们分析了 Spring 如何从容器中找到切面配置类对应的 Bean,以及从中找到配置增强逻辑的方法,本文将分析最后一个关键的步骤,就是如何将找到的增强方法封装成 Advisor。

创建 Advisor

作为上一篇的后续,本文的分析从 ReflectiveAspectJAdvisorFactory 的getAdvisor方法入手。

// org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisor@Override@NullablepublicAdvisorgetAdvisor(MethodcandidateAdviceMethod, MetadataAwareAspectInstanceFactoryaspectInstanceFactory,
intdeclarationOrderInAspect, StringaspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
AspectJExpressionPointcutexpressionPointcut=getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut==null) {
returnnull;
   }
returnnewInstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

首先对增强方法所在的切面配置类进行验证,然后通过getPointcut获取增强方法对应的切入点信息。之后,在确保能够获取到切入点信息的情况下,通过构造方法创建 Advisor 对象,这里的 Advisor 对象类型是 InstantiationModelAwarePointcutAdvisorImpl 类型,其中包含了切入点信息、增强方法、切面配置类的 Bean 名称等重要信息。

因此,这里的重点有两个,一个是通过getPointcut方法获取切入点信息,另一个就是 Advisor 的创建,我们分别来看。

获取切入点信息

我们先进入getPointcut方法。

// org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getPointcut@NullableprivateAspectJExpressionPointcutgetPointcut(MethodcandidateAdviceMethod, Class<?>candidateAspectClass) {
AspectJAnnotation<?>aspectJAnnotation=AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation==null) {
returnnull;
   }
AspectJExpressionPointcutajexp=newAspectJExpressionPointcut(candidateAspectClass, newString[0], newClass<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory!=null) {
ajexp.setBeanFactory(this.beanFactory);
   }
returnajexp;
}

首先,会通过 AbstractAspectJAdvisorFactory 类的findAspectJAnnotationOnMethod方法获取方法上标记的增强注解信息,我们进入这个方法看一下。

@NullableprotectedstaticAspectJAnnotation<?>findAspectJAnnotationOnMethod(Methodmethod) {
for (Class<?>clazz : ASPECTJ_ANNOTATION_CLASSES) {
AspectJAnnotation<?>foundAnnotation=findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation!=null) {
returnfoundAnnotation;
      }
   }
returnnull;
}

其中的常量定义如下,也就是@Pointcut以及五种增强类型对应的注解。

privatestaticfinalClass<?>[] ASPECTJ_ANNOTATION_CLASSES=newClass<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};

findAspectJAnnotationOnMethod方法中,会遍历这些注解类,对于每一个遍历到的注解类型,如果发现给定的方法上标记了这个注解,那么就返回封装好的 AspectJAnnotation 注解信息,如果都没有匹配到,则返回空。

这个逻辑中还隐含了一个逻辑,如果一个注解在给定的方法上找到了,那么后面就不再判断了。

回到getPointcut方法中,如果上一步没有获取到注解信息,那么就说明当前要处理的方法不是一个增强方法,就不再处理了。

接下来,创建一个 AspectJExpressionPointcut 对象ajexp,将切面类的类型、增强注解中的切入点表达式、当前的 Spring 容器等赋值到ajexp的对应属性,最后,将它作为getPointcut方法的结果返回。

至此,getPointcut方法就结束了,最终返回一个 AspectJExpressionPointcut 类型的结果或者一个空值。我们接着看getAdvisor方法的另外一个关键步骤。

创建 Advisor 对象

如果上一步获取的切入点信息不为空的话,则会创建 Advisor 对象并返回。

returnnewInstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);

这里创建 Advisor 的方式是通过构造方法创建,它的具体类型是 InstantiationModelAwarePointcutAdvisorImpl,我们现通过继承关系了解一下这个类。

image.png

先对它的继承关系大致有个印象,然后我们看构造方法中的逻辑。

publicInstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcutdeclaredPointcut,
MethodaspectJAdviceMethod, AspectJAdvisorFactoryaspectJAdvisorFactory,
MetadataAwareAspectInstanceFactoryaspectInstanceFactory, intdeclarationOrder, StringaspectName) {
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.PointcutpreInstantiationPointcut=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=newPerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy=true;
   }
else {
// A singleton aspect.this.pointcut=this.declaredPointcut;
this.lazy=false;
this.instantiatedAdvice=instantiateAdvice(this.declaredPointcut);
   }
}

其实并没有什么复杂的逻辑,就是将参数传入的各项信息复制给了对应的成员变量。

到这里,getAdvisor方法的原理也就分析完了。

findCandidateAdvisors 方法小结

经过三篇文章的分析,我们已经了解了 AnnotationAwareAspectJAutoProxyCreator 类中的findCandidateAdvisors方法作为被后处理器调用的方法,如何从当前容器中查找到所有的切面配置类,然后再从这些类中找到所有的增强方法并封装成对应的 Advisor 对象返回。

为了让后续的分析更加连贯,我们再回顾一下之前的内容。

  1. 我们最初从 AbstractAuthProxyCreator 类的wrapIfNecessary方法为入口,分析 Spring 如何在后处理器中为已经完成初始化的 Bean 实例创建 AOP 代理。
  2. wrapIfNecessary方法中,会通过getAdvicesAndAdvisorsForBean方法获取能够对当前 Bean 实例进行增强的 Advisor,这个工作又被交给了findEligibleAdvisors方法。
  3. findEligibleAdvisors方法中的第一步,就是获取到容器中所有能获取到的 Advisor。

前面的几篇文章,就是在分析如何从容器中获取到全部的 Advisor,我会将最近的几篇文章链接放在文章末尾以供回顾。下一篇讲开始分析之后的内容,也就是如何从全部的 Advisor 中找到与当前处理的 Bean 实例匹配的 Advisor。

总结

本文分析了 Spring 查找注解配置的 AOP 切面信息的最后一部分,Spring 会将查找到的所有增强类中的方法进行筛选,找到所有的增强方法,并将其封装成对应的 Advisor。


相关阅读:

目录
相关文章
|
16天前
|
小程序 JavaScript Java
高校宿舍信息|基于Spring Boot的高校宿舍信息管理系统的设计与实现(源码+数据库+文档)
高校宿舍信息|基于Spring Boot的高校宿舍信息管理系统的设计与实现(源码+数据库+文档)
22 0
|
1天前
|
设计模式 Java 关系型数据库
Spring的配置文件,如何配置端口号,,properties,yml获取配置项等方法,外观模式及其优缺点,日志代表的信息
Spring的配置文件,如何配置端口号,,properties,yml获取配置项等方法,外观模式及其优缺点,日志代表的信息
|
3天前
|
Java Spring
【JavaEE进阶】 Spring AOP源码简单剖析
【JavaEE进阶】 Spring AOP源码简单剖析
|
13天前
|
Java Spring 容器
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
Spring核心概念、IoC和DI的认识、Spring中bean的配置及实例化、bean的生命周期
33 0
|
14天前
|
Java Python Spring
小唐开始学 Spring Boot——(2)Spring Boot核心配置与注解
小唐开始学 Spring Boot——(2)Spring Boot核心配置与注解
|
16天前
|
小程序 JavaScript Java
小程序商城|基于Spring Boot的智能小程序商城的设计与实现(源码+数据库+文档)
小程序商城|基于Spring Boot的智能小程序商城的设计与实现(源码+数据库+文档)
24 0
小程序商城|基于Spring Boot的智能小程序商城的设计与实现(源码+数据库+文档)
|
16天前
|
安全 JavaScript Java
在线问卷调查|基于Spring Boot的在线问卷调查系统的设计与实现(源码+数据库+文档)
在线问卷调查|基于Spring Boot的在线问卷调查系统的设计与实现(源码+数据库+文档)
27 0
|
22天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
72 0
|
22天前
|
缓存 安全 Java
Spring Boot 面试题及答案整理,最新面试题
Spring Boot 面试题及答案整理,最新面试题
157 0
|
22天前
|
存储 JSON Java
SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间
SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间
58 2