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

news2024/9/30 3:19:37

人人都能看懂的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。

在这里插入图片描述

两种配置方式

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

  • xml
  • 注解

在这里插入图片描述

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

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

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

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

在这里插入图片描述

保存BeanDefinition

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

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

在这里插入图片描述

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

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

源码走读

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

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

xml配置方式

整体流程

在这里插入图片描述

上面这幅图是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加载解析的入口

在这里插入图片描述

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

在这里插入图片描述

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

创建XmlBeanDefinitionReader,加载配置文件

在这里插入图片描述

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

在这里插入图片描述

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

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

在这里插入图片描述
代码调用流程:加载成Document,调用registerBeanDefinitions(doc, resource) => 创建BeanDefinitionDocumentReader,调用documentReader.registerBeanDefinitions(doc, createReaderContext(resource)) => 获取Document里面的根标签,调用doRegisterBeanDefinitions(…) => 创建BeanDefinitionParserDelegate,调用parseBeanDefinitions(root, this.delegate) => 获取根标签下的所有子标签,然后遍历所有子标签,进行解析

在这里插入图片描述

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

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

解析标签,生成BeanDefinition

在这里插入图片描述

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

在这里插入图片描述

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

注册BeanDefinition到容器

在这里插入图片描述

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

在这里插入图片描述

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

注解配置方式

整体流程

在这里插入图片描述

上面这幅图是注解配置方式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

在这里插入图片描述

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

在这里插入图片描述

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

注册ConfigurationClassPostProcessor

在这里插入图片描述

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

在这里插入图片描述

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

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

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

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

在这里插入图片描述

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

在这里插入图片描述

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

配置信息解析

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

总结

在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/348878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习基础

一、基本概念 1 学习的概念 1975年图灵奖获得者、1978年诺贝尔经济学奖获得者、著名学者赫伯特.西蒙 (Herbert Simon) 曾下过一个定义: 如果一个系统&#xff0c;能够通过执行某个过程&#xff0c;就此改进了它的性能&#xff0c;那么这个过程就是学习.由此可看出&#xff0c;…

思科基础组面试(部分)

面了三轮&#xff0c;前面两轮因为录的视频坏了&#xff0c;很多问题忘了。 Round 1 Q:举例说明为什么hashmap线程不安全 A1: JDK1.8 HashMap线程不安全体现在&#xff1a;数据覆盖: 其中第六行代码是判断是否出现hash碰撞&#xff0c;假设两个线程A、B都在进行put操作&#…

软件测试未来发展趋势怎么样

未来&#xff0c;互联网技术是很多企业能够活下去的关键点。互联网技术成为新的基建&#xff0c;互联网“基建”化就决定了软件测试行业的缺口会一直扩大。 并且&#xff0c;软件测试岗位&#xff0c;已不仅局限于互联网企业&#xff0c;现已逐步深入到实体产业&#xff0c;金…

【安全等保】安全等保二级和三级哪个高?哪个费用更高?

等保政策已经严格落地执行了&#xff0c;各大企业纷纷接到了过等保的通知&#xff0c;但有的估计是第一次听到等保&#xff0c;对于等保相关政策都是非常蒙圈的。这不不少企业相关负责人在问&#xff0c;安全等保二级和三级哪个高&#xff1f;哪个费用更高&#xff1f;这里我们…

分布式文件系统使用——MinIO

分布式文件系统使用——MinIO 1 分布式文件系统 1.1 概念 常见的文件系统&#xff1a;FAT16/FAT32、NTFS、HFS、UFS、APFS、XFS、Ext4等 。 现在有个问题&#xff0c;一此短视频平台拥有大量的视频、图片&#xff0c;这些视频文件、图片文件该如何存储呢&#xff1f;如何存储…

04- 根据Xgboost集成算法预测还贷能力 (项目四)

筛选最佳参数: # 对于max_depth和min_child_weight查找最好的参数 param_grid { max_depth:range(3,10,2),min_child_weight:range(1,6,2)}model XGBClassifier(learning_rate 0.1,n_estimators100,max_depth5,use_label_encoderFalse,min_child_weight1,gamma0,subsample0…

状态管理VueX

哈喽~大家好&#xff0c;这篇来看看状态管理VueX。 &#x1f947;个人主页&#xff1a;个人主页​​​​​ &#x1f948; 系列专栏&#xff1a;【专栏】 &#x1f949;与这篇相关的文章&#xff1a; SpringCloud Sentinel 使用SpringClou…

java整数转罗马数字

罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&#xff0c; 罗马数字 2 写做 II &#xff0c;即为两个并列的 1。12 写做 XII &#xff0c;即为…

低版本jQuery导致XSS Nuclei FUZZ POC

目录 1.前言 2. Nuclei FUZZ jQuery XSS POC 3.漏洞验证 4.修复建议 1.前言 我记得以前用那些漏扫工具时时常会报一个低版本jQuery的安全问题,当时还不会验证。直到有一天,它托梦给我。我悟了。低版本jQuery导致XSS POC文件文末获取。

java.io.IOException: Could not find resource com/itheima/mapper/UserMapper.xml

问题&#xff1a;Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/itheima/mapper/UserMapper.xml问题描述&#xff1a;找不到UserMapper解决方案&#xff1a;这是我原来的路径这是我改后的路径&#xff08;很重要&#xff…

羊了个羊游戏开发教程2:随机生成卡牌

本文首发于微信公众号&#xff1a; 小蚂蚁教你做游戏。欢迎关注领取更多学习做游戏的原创教程资料&#xff0c;每天学点儿游戏开发知识。嗨&#xff01;大家好&#xff0c;我是小蚂蚁。上一节教程里我们实现了游戏中最难的地方——堆叠牌的拾取&#xff0c;这节教程我们来继续学…

关于CountDownLatch

关于CountDownLatchCountDownLatch 是什么CountDownLatch 如何工作CountDownLatch API使用示例与 Join 的区别CountDownLatch 是什么 CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如&#xff0c;应用程序的主线程希望在负责启动框架服务的线程已…

强化学习基础

强化学习的三种方法 基于价值&#xff08;value-based&#xff09; 基于策略&#xff08;policy-based&#xff09; 基于模型&#xff08;model-based&#xff09; 一 基于价值的方法 基于价值 (Value-Based)这种方法&#xff0c;目标是优化价值函数V(s)。 价值函数会告诉我们…

LeetCode 1669. 合并两个链表(C++)

思路&#xff1a; 该题思路很简单&#xff0c;对于单向链表&#xff0c;先遍历到指定的右边界的位置b1&#xff0c;做好标记供连接&#xff1b; 然后对于a-1位置的结点&#xff0c;连接list2&#xff0c;并最后连接后半段的list1 1.题目如下&#xff1a; 给你两个链表 list1 …

ATX agent+UIautomation2 自动化测试介绍

目前ATXUIautomator2 处于自动化界的浪口风尖&#xff0c;现在有幸终于有时间对ATX进行了粗浅的了解 为什么要用ATX ATXUIautomator2的优势&#xff1a; 1.速度吊打appnium&#xff0c;群里面的人这样说的 运行速度快&#xff0c;比Appium运行速度快了好多。&#xff08;用…

分布式架构

目录 一、前言 二、分布式架构的发展历史 三、分布式架构发展的里程碑 四、分布式系统的意义 五、分布式架构的常见概念 六、分布式领域中冯诺依曼模型的变化 七、分布式系统的难点 八、总结 一、前言 ​  我们都知道&#xff0c;当今无论在BAT这样的大公司&#xff…

Install Linux on Windows with WSL2 (使用 WSL2 在 Windows 上安装 Linux)

Install Linux on Windows with WSL2 (使用 WSL2 在 Windows 上安装 Linux)https://learn.microsoft.com/en-us/windows/wsl/ 在 Windows 上运行 Linux - 在 Windows 11 上运行 Ubuntu 20.04 LTS Developers can access the power of both Windows and Linux at the same tim…

实验十三、阻容耦合共射放大电路的频率响应

一、题目 利用 Multism 从以下几个方面研究图1所示的阻容耦合共射放大电路的频率响应。图1阻容耦合共射放大电路图1\,\,阻容耦合共射放大电路图1阻容耦合共射放大电路&#xff08;1&#xff09;设 C1C210μFC_1C_210\,\textrm{μF}C1​C2​10μF&#xff0c;分别测试它们所确定…

6万字电力行业系统解决方案光伏电站综合安防系统解决方案

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 目录 第 一 章背景与需求 1.1行…

2023年收银管理系统排行榜新鲜出炉

随着新零售的模式普及&#xff0c;越来越多的零售店选择了用收银系统代替收银机。因为收银系统不仅具备收银等功能&#xff0c;其实还有各种店铺数据管理功能、经销商信息管理&#xff0c;销售数据分析等许多功能等。所以如果想清晰地知道门店每天盈利情况和库存情况和采购过程…