自动扫描并注册Bean

前言

在上一章节结束之后,我们其实已经完成了 IoC 和 AOP 的全部核心内容。但是现在还需要使用 Spring.xml 进行配置。思考思考我们平时最多是怎么用 Spring 完成信息配置的?一是注解,二是 property。

为了在核心逻辑上填充一些自动化的功能,所需要的知识有:

  • 包的扫描注册
  • 注解配置使用
  • 占位符属性的填充

设计

为了可以简化 Bean 对象的配置,达到整个 Bean 对象的注册都是自动扫描的。最基本的元素包括:

  • 扫描路径入口
  • XML 解析扫描信息
  • 给需要扫描的 Bean 对象做注解标记
  • 扫描 Class 对象摘取 Bean 注册的基本信息
  • 组装 Bean 注册信息
  • 注册 Bean 对象

除此之外再顺带解决一个配置中占位符属性的知识点,比如可以通过 ${token} 给 Bean 对象注入进去属性信息,那么这个操作需要用到 BeanFactoryPostProcessor,因为它可以处理 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制

image-20230215094554414

结合 bean 的生命周期,包扫描只不过是扫描特定注解的类,提取类的相关信息组装成 BeanDefinition 注册到容器中。

在 XmlBeanDefinitionReader 中解析<context:component-scan />标签,扫描类组装 BeanDefinition 然后注册到容器中的操作在 ClassPathBeanDefinitionScanner#doScan 中实现。

  • 自动扫描注册主要是扫描添加了自定义注解的类,在 xml 加载过程中提取类的信息,组装 BeanDefinition 注册到 Spring 容器中。
  • 所以我们会用到 <context:component-scan /> 配置包路径并在 XmlBeanDefinitionReader 解析并做相应的处理。这里的处理会包括对类的扫描、获取注解信息等
  • 最后还包括了一部分关于 BeanFactoryPostProcessor 的使用,因为我们需要完成对占位符配置信息的加载,所以需要使用到 BeanFactoryPostProcessor 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,修改 BeanDefinition 的属性信息。这一部分的实现也为后续处理关于占位符配置到注解上做准备

处理对象扫描装配

XML 解析类 XmlBeanDefinitionReader 对 ClassPathBeanDefinitionScanner#doScan 的使用

doScan 方法处理所有指定路径下添加注解的类,拆解出类的信息{ 名称 beanName,作用范围 scope }.在进行创建 BeanDefinition 用于 Bean 对象的注册操作。

详细代码:

  • doScan 中,findCandidateComponents 负责将所有标注了 @Component 注解的类 通过 ClassUtil.scanPackageByAnnotation(basePackage, Component.class) 解析出来, Set<Class<?>> 维护 Class,Set 维护以及实例化好的 BeanDefinition(clazz)
  • 对每个 BeanDefinition 而言,需要解析其 scope(可为 null)和 beanName。他们的解析都是通过获取注解内容的方式 getAnnotation(@interface.class)

PropertyPlaceholderConfigurer

目前看上去像一块单独的内容,后续会把这块的内容与自动加载 Bean 对象进行整合,也就是可以在注解上使用占位符配置一些在配置文件里的属性信息。

他所做的步骤如下:

  1. 读取属性文件资源,通过 load 方式将信息加载到 properties 类中
  2. 通过一些简单逻辑操作将占位符 “{@Value}” 替换成 “实际内容”

详细代码:

资源读取

迭代读取 XML 配置信息的代码,使用 dom4j 方式解析 xml,不仅提高代码简洁性,也提升易读性

<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>