人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册

人人都能看懂的Spring源码解析,扫描加载BeanDefinition的过程

  • 原理解析
    • 什么是BeanDefinition?
    • 两种配置方式
    • 扫描并读取配置信息,解析成BeanDefinition
    • 保存BeanDefinition
  • 源码走读
    • xml配置方式
      • 整体流程
      • 示例代码
      • BeanDefinition加载解析的入口
      • 创建XmlBeanDefinitionReader,加载配置文件
      • 配置文件加载成Document,遍历解析里面的标签
      • 解析标签,生成BeanDefinition
      • 注册BeanDefinition到容器
    • 注解配置方式
      • 整体流程
      • 示例代码
      • 创建AnnotatedBeanDefinitionReader
      • 注册ConfigurationClassPostProcessor
      • 解析启动配置类为BeanDefinition,并注册到容器中
      • ConfigurationClassPostProcessor,注解版配置信息的解析入口
      • 配置信息解析
  • 总结

每一位看到本篇文章的人,你们好,我是黄俊懿。

之前写过两篇关于Spring原理解析的文章,是以对新手友好的出发点去写的,以画图的形式进行讲解,没有对Spring的源码进行详细的解析,目的是希望一些没有看过Spring源码的小伙伴能够很好的理解。

人人都能看懂的Spring底层原理,看完绝对不会懵逼

简单易懂的Spring扩展点详细解析,看不懂你来打我

然后从本篇文章开始,我打算写一些关于Spring源码解析的内容,也是尽量做到对新手友好,当然看过Spring源码的小伙伴也可以拿来作为复习。

希望能坚持下去,帮助大家学习或者复习Spring的知识,同时自己也有所收获。

本篇文章是关于配置解析与BeanDefinition加载注册的源码解析,也就是从xml配置或者注解配置被解析为BeanDefinition放入容器中的这个过程。

在这里插入图片描述

原理解析

什么是BeanDefinition?

相当于是bean的设计图纸,Spring要实例化和初始化bean,需要先有bean的设计图纸,好比建房子之前,先要有房子的设计图纸。

在这里插入图片描述

而BeanDefinition中保存了bean的各种配置属性,Spring会根据其中的配置属性,去实例化和初始化bean。

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第1张图片

两种配置方式

我们可以通过两种方式进行配置:

  • xml
  • 注解

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第2张图片

不管使用哪种配置方式,在创建Spring应用上下文的时候,都要指定配置解析的入口。如果是xml配置方式,配置解析的入口就是xml配置文件,如果是注解配置方式,配置解析入口就是 @Configuration注解修饰的配置类

扫描并读取配置信息,解析成BeanDefinition

在创建Spring应用上下文的时,指定了配置文件或者配置类之后,接下来就是扫描并读取配置信息,解析成BeanDefinition

在Spring内部,定义了一个组件,专门负责bean定义配置的扫描解析,并将bean的配置信息解析成BeanDefinition,然后放入容器中,那就是BeanDefinitionReader

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第3张图片

保存BeanDefinition

扫描完配置,解析成BeanDefinition之后,就要把这些BeanDefinition保存到Spring容器中。

Spring内部定义了另外一个组件BeanDefinitionRegistry(bean定义注册表),用于注册BeanDefinition,而实现类就是DefaultListableBeanFactory,使用一个Map结构存放解析出来的BeanDefinition,key是String类型的beanName,value是BeanDefinition类型

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第4张图片

DefaultListableBeanFactory中关于BeanDefinition的重要属性和方法:

  • beanDefinitionMap就是用于存放注册进来的BeanDefinition的哈希表
  • beanDefinitionNames用于存放注册进来的BeanDefinition的beanName
  • registerBeanDefinition(String, BeanDefinition) 就是用于注册BeanDefinition的方法

源码走读

接下来是源码走读,看一下Spring里面源码的调用流程。

注意:代码调用流程并不重要,不需要硬记,只要知道里面大概干了啥就行,代码调用流程只是用于证明里面确实是这么干的。

xml配置方式

整体流程

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第5张图片

上面这幅图是xml配置方式BeanDefinition解析的整体流程,每一步用不同颜色标记,防止迷路。

示例代码

com.demo.xml.Main

public class Main {

    public static void main(String[] args) {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Person person = context.getBean(Person.class);
        System.out.println(person);

    }

}

beans.xml

<beans ...">

    <bean id="person" class="com.demo.Person"/>
    
beans>

com.demo.Person

public class Person {
}

BeanDefinition加载解析的入口

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第6张图片

代码调用流程:ClassPathXmlApplicationContext的构造方法 => refresh() => obtainFreshBeanFactory() => loadBeanDefinition(beanFactory)

在这里插入图片描述

loadBeanDefinition(beanFactory) 方法就是BeanDefinition加载解析的入口。

创建XmlBeanDefinitionReader,加载配置文件

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第7张图片

代码调用流程:创建XmlBeanDefinitionReader,调用loadBeanDefinitions(beanDefinitionReader) => reader.loadBeanDefinitions(configLocations) => 读取配置文件,以流的形式加载到内存,doLoadBeanDefinitions(inputSource, encodedResource.getResource())

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第8张图片

可以看到,loadBeanDefinition(beanFactory)方法方法里面就是创建了一个XmlBeanDefinitionReader,并调用XmlBeanDefinitionReader的loadBeanDefinitions方法,里面读取配置文件并加载到内存。

配置文件加载成Document,遍历解析里面的标签

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第9张图片
代码调用流程:加载成Document,调用registerBeanDefinitions(doc, resource) => 创建BeanDefinitionDocumentReader,调用documentReader.registerBeanDefinitions(doc, createReaderContext(resource)) => 获取Document里面的根标签,调用doRegisterBeanDefinitions(…) => 创建BeanDefinitionParserDelegate,调用parseBeanDefinitions(root, this.delegate) => 获取根标签下的所有子标签,然后遍历所有子标签,进行解析

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第10张图片

XmlBeanDefinitionReader里面,就是把配置文件加载成Document对象,然后获取里面的根标签(也就是标签),然后获取根标签下的所有子标签,遍历所有的子标签(标签),进行解析

中间创建的BeanDefinitionParserDelegate对象,是用于后面解析标签为BeanDefinition的。

解析标签,生成BeanDefinition

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第11张图片

代码调用流程:processBeanDefinition(ele, delegate) => delegate.parseBeanDefinitionElement(ele) => parseBeanDefinitionElement(ele, beanName, containingBean) => createBeanDefinition(className, parent) => BeanDefinitionReaderUtils.createBeanDefinition(…) => new GenericBeanDefinition()

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第12张图片

可以看到,最后创建的BeanDefinition类型是GenericBeanDefinition,然后解析标签上的各种属性,赋值到BeanDefinition对应的属性上。

注册BeanDefinition到容器

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第13张图片

代码调用流程:BeanDefinitionReaderUtils.registerBeanDefinition(…) => registry.registerBeanDefinition(…) =>
this.beanDefinitionMap.put(beanName, beanDefinition)

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第14张图片

可以看到,里面调用的时BeanDefinitionRegistry的registerBeanDefinition方法,进入到DefaultListableBeanFactory#registerBeanDefinition方法里面,把BeanDefinition放入到DefaultListableBeanFactory里面的beanDefinitionMap属性中,该属性是一个Map类型,key是beanName,value是BeanDefinition

注解配置方式

整体流程

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第15张图片

上面这幅图是注解配置方式BeanDefinition解析的整体流程,每一步用不同颜色标记,防止迷路。

示例代码

com.demo.annotation.Main

public class Main {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = context.getBean(Person.class);
        System.out.println(person);

    }

}

com.demo.annotation.MainConfig

@Configuration
public class MainConfig {
    @Bean
    public Person person() {
        return new Person();
    }
}

创建AnnotatedBeanDefinitionReader

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第16张图片

代码调用流程:AnnotationConfigApplicationContext构造方法 => this() => 创建AnnotatedBeanDefinitionReader

在这里插入图片描述

AnnotatedBeanDefinitionReader也是一个BeanDefinitionReader,主要是针对注解版配置的解析,也具有解析配置信息为BeanDefinition并注册到容器的功能,但其实它没有实现BeanDefinitionReader接口。

注册ConfigurationClassPostProcessor

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第17张图片

代码调用流程:AnnotatedBeanDefinitionReader构造方法 => this(…) => AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry) => 注册ConfigurationClassPostProcessor到容器中

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第18张图片

ConfigurationClassPostProcessor的作用就是解析@Configuration注解修饰的配置类里的配置信息,生成BeanDefinition,注册到容器中。

@Configuration注解修饰的配置类,相当于xml配置方式下的xml配置文件。

解析启动配置类为BeanDefinition,并注册到容器中

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第19张图片

代码调用流程:register(componentClasses) => this.reader.register(componentClasses)

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第20张图片

调用了AnnotatedBeanDefinitionReaderregister方法,解析配置类为BeanDefinition,并注册到容器中。

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第21张图片

代码调用流程:registerBean(componentClass) => doRegisterBean(…) => 创建AnnotatedGenericBeanDefinition类型的BeanDefinition,并配置该BeanDefinition,然后注册到容器中

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第22张图片

这里就是根据参数传进来的配置类的类型,创建BeanDefinition,BeanDefinition的类型是AnnotatedGenericBeanDefinition,是注解版的BeanDefinition,然后对其进行属性配置,最后注册到容器中。

ConfigurationClassPostProcessor,注解版配置信息的解析入口

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第23张图片

代码调用流程:refresh() => invokeBeanFactoryPostProcessors(beanFactory) => PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()) => invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry) => postProcessor.postProcessBeanDefinitionRegistry(registry) => ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第24张图片

前面已经把@Configuration注解修饰的启动配置类,解析成BeanDefinition,注册到了Spring容器中,那么接下来就要对启动配置类里的配置信息进行解析,而解析入口,就是ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法

配置信息解析

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第25张图片

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor又继承了BeanFactoryPostProcessor接口,所以ConfigurationClassPostProcessor也是一个Bean工厂后置处理器

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第26张图片

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,该方法可以往Spring容器中注册一些BeanDefinition。ConfigurationClassPostProcessor将会在该方法中解析@Configuration注解修饰的配置类里的配置信息,注册BeanDefinition到Spring容器中,如果遇到了@ComponentScan注解,则会进行包扫描,解析@Component注解修饰的类为BeanDefinition,注册到容器中,如果又遇到了其他被@Configuration注解修饰的类,会继续进行解析

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第27张图片

但是因为ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry里面的逻辑比较复杂,解析里面的逻辑可能需要大量的篇幅,所以我感觉单独开一篇博客进行解析会比较好,所以这篇博客就到这里结束吧,ConfigurationClassPostProcessor里面的源码解析将会放到下一篇博客。

总结

人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册_第28张图片

最后总结一下:xml配置方式由XmlBeanDefinitionReader进行配置文件的解析与BeanDefinition的注册,而注解配置方式则由AnnotatedBeanDefinitionReader将启动配置类解析成BeanDefinition注册到容器中,再由ConfigurationClassPostProcessor进行@Configuration注解修饰的配置类的配置信息解析,生成BeanDefinition注册到容器中。

你可能感兴趣的:(Spring,spring,java,spring,boot,后端,spring源码解析)