Spring 源码阅读 51:查找注解配置的切面增强逻辑(1)- 查找配置类

简介: 本文分析了用于创建 AOP 代理的后处理器是如何查找基于注解配置的切面信息的,这是前半部分,主要包含切面配置类的查找和解析。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 50:解析 XML 中的切面配置

相关内容:Spring 源码阅读 49:AOP 代理创建的过程

概述

在【Spring 源码阅读 49:AOP 代理创建的过程 】一文中,我们分析了 AOP 代理创建的大致流程,其中提到了,无论是通过 XML 还是注解配置的切面,它们的工作原理都是大致一样的,其中有一个比较大的区别就是在增强的查找方面。

在上一篇中,我们分析了通过 XML 配置切面的情况下,切面配置信息是如何被 Spring 从 XML 文件中解析并加载的,以及负责创建 AOP 的后处理器是如何在处理逻辑中找到它们的。与之不同的是,通过注解配置的切面信息会是以一个 Java 类为载体的,而这个类会作为一个普通的 Bean 被注册到 Spring 容器中。

这篇文章,我们分析基于注解配置的切面信息,是如何被创建 AOP 的后处理器找到的。

查找过程

与上一篇一样,我们还是从findCandidateAdvisors方法入手,在基于注解配置切面的情况下,负责创建 AOP 代理的后处理器是 AnnotationAwareAspectJAutoProxyCreator,它自己重写了父类的findCandidateAdvisors方法,我们找到其实现方法。

@OverrideprotectedList<Advisor>findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.List<Advisor>advisors=super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.if (this.aspectJAdvisorsBuilder!=null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
returnadvisors;
}

父类的 findCandidateAdvisors 方法

首先,它调用了父类的findCandidateAdvisors方法,它的父类是 AspectJAwareAdvisorAutoProxyCreator,其中并没有这个方法的实现,而父类的父类 AbstractAdvisorAutoProxyCreator 有这个方法的实现逻辑。因此,这里调用了 AbstractAdvisorAutoProxyCreator 的findCandidateAdvisors方法。

image.png

而 AbstractAdvisorAutoProxyCreator 的findCandidateAdvisors方法,就是我们在上一篇【Spring 源码阅读 50:解析 XML 中的切面配置 】中分析过的用于查找 XML 配置的切面信息的方法。因此,这里的逻辑是先查找 XML 配置的切面信息,得到一个 Advisor 类型的集合advisors

接下来,通过成员变量aspectJAdvisorsBuilderbuildAspectJAdvisors方法,获取一个 Advisor 列表,添加到之前的advisors集合中,最后将advisors作为方法的结果返回。

所以,接下来的重点就是分析buildAspectJAdvisors方法。

buildAspectJAdvisors 方法

我们找到方法的源码。

image.png

从方法的注释中可以看到,这个方法的作用就是在容器中找到所有基于 Aspect 注解配置的切面信息,并为每一个增强方法创建一个 Advisor,以列表的形式返回结果。

这个方法比较长,我们还是分段来分析。

List<String>aspectNames=this.aspectBeanNames;
if (aspectNames==null) {
synchronized (this) {
aspectNames=this.aspectBeanNames;
if (aspectNames==null) {
// 省略中间的部分。。。      }
   }
}

首先会从成员变量aspectBeanNames中获取所有切面 Bean 得名称,如果是第一次走到这个方法,这里的列表肯定是空的,此时会进入if语句块的逻辑。进入if语句块中。

List<Advisor>advisors=newArrayList<>();
aspectNames=newArrayList<>();
String[] beanNames=BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);

首先定义了一个 Advisor 列表,并初始化了aspectNames列表,然后通过 BeanFactoryUtils 的beanNamesForTypeIncludingAncestors方法,获取到了容器中所有的 Bean 名称,得到beanNames数组。

for (StringbeanName : beanNames) {
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.Class<?>beanType=this.beanFactory.getType(beanName);
if (beanType==null) {
continue;
   }
if (this.advisorFactory.isAspect(beanType)) {
// 省略部分语句   }
}

接下来,遍历上一步获取到的beanNames数组。

对于每一个beanName,先通过isEligibleBean方法筛选,这个方法默认返回true,因此不会执行continue。然后,判断它对应的 Bean 的类型不为空。

在第三个if判断条件中,通过isAspect方法判断了当前遍历到的beanName对应的类型是不是一个切面配置的类,我们可以进入isAspect方法,看一下具体的判断逻辑。

// org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect@OverridepublicbooleanisAspect(Class<?>clazz) {
return (hasAspectAnnotation(clazz) &&!compiledByAjc(clazz));
}
// org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#hasAspectAnnotationprivatebooleanhasAspectAnnotation(Class<?>clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) !=null);
}

其实逻辑很简单,即使看这个类型是不是被标记了@Aspect注解。

回到前面的方法中,如果此时判断一个类型是切面配置的类型,则执行if语句块中的逻辑。

if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadataamd=newAspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() ==PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactoryfactory=newBeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
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)) {
thrownewIllegalArgumentException("Bean with name '"+beanName+"' is a singleton, but aspect instantiation model is not singleton");
      }
MetadataAwareAspectInstanceFactoryfactory=newPrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
   }
}

首先,将当前的beanName添加到aspectNames集合中,然后使用aspectNamesbeanType创建一个 AspectMetadata 类型的切面元信息对象amd

接下来,通过amd判断切面的实例化模型,通常情况下都是单例的,也就是会进入if-else语句块的if部分。其中,会创建一个 MetadataAwareAspectInstanceFactory 的factory,然后通过advisorFactorygetAdvisors方法,获取包含当前类型的所有增强方法对应的 Advisor 组成的列表。然后判断当前遍历到的类型是不是单例的,如果是,就将获取到的 Advisor 列表添加到advisorsCache缓存中,如果不是,就将工厂对象factory添加到aspectFactoryCache缓存中。

另外,对于当前类型的切面实例化模型不是单例的情况,则不管当前类型是否单例,都将其工厂对象factory添加到aspectFactoryCache缓存中。

在此期间,所有获得到的 Advisor 都会被添加到advisors集合中。

for (StringbeanName : beanNames) {
// 省略中间部分。。。}
this.aspectBeanNames=aspectNames;
returnadvisors;

在结束for循环后,advisors会作为buildAspectJAdvisors方法的结果返回。

getAdvisors 方法

以上的逻辑中,我们还需要深入分析一下getAdvisors方法,是如何从配面配置类的信息中获取到 Advisor 列表的。这部分我会放到下一篇再深入分析。

总结

本文分析了用于创建 AOP 代理的后处理器是如何查找基于注解配置的切面信息的,这是前半部分,主要包含切面配置类的查找和解析。下一篇将继续分析,针对每一个切面配置类,怎样从中找到所有的增强逻辑并封装成对应的 Advisor 对象。


目录
相关文章
|
6天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
1天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
8 2
|
1天前
|
消息中间件 开发框架 Java
什么是Spring Boot 自动配置?
Spring Boot 是一个流行的 Java 开发框架,它提供了许多便利的功能和工具,帮助开发者快速构建应用程序。其中一个最引人注目的特性是其强大的自动配置功能。
6 0
|
2天前
|
JSON 前端开发 Java
【JAVA进阶篇教学】第七篇:Spring中常用注解
【JAVA进阶篇教学】第七篇:Spring中常用注解
|
2天前
|
安全 Java 开发者
深入理解Spring Boot配置绑定及其实战应用
【4月更文挑战第10天】本文详细探讨了Spring Boot中配置绑定的核心概念,并结合实战示例,展示了如何在项目中有效地使用这些技术来管理和绑定配置属性。
10 1
|
4天前
|
Java Spring
Spring文件配置以及获取
Spring文件配置以及获取
11 0
|
4天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
5天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
17 2
|
5天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
6天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
16 2