spring IOC初始化过程

1、开篇
· IoC是如何工作的?
· Resource定位
· 载入BeanDefinition
· 将BeanDefiniton注册到容器

2、IoC是如何工作的?
如图1所示,通过ApplicationContext创建Spring容器,该容器会读取配置文件"/beans.xml",并统一管理由该文件中定义好的bean实例对象,如果要获取某个bean实例,使用getBean方法就行了。假设将User配置在beans.xml文件中,之后不需使用new User()的方式创建实例,而是通过ApplicationContext容器来获取User的实例。
在这里插入图片描述
图1 通过spring容器创建实例
下面就来刨析创建IoC容器经历的几个阶段:Resource定位、载入BeanDefinition、将BeanDefiniton注册到容器。
3、Resource定位
Resource是Spring中用于封装I/O操作的接口。在创建Spring容器时,会去访问XML配置文件,还可以通过文件类型、二进制流、URL等方式访问资源。这些都可以理解为Resource,其体系结构如图2所示:
· FileSystemResource:以文件绝对路径进行资源访问。
· ClassPathResourcee:以类路径的方式访问资源。
· ServletContextResource:web应用根目录的方式访问资源。
· UrlResource:访问网络资源的实现类。  
· ByteArrayResource: 访问字节数组资源的实现类。

图2 Resouce资源访问类型
那么这些类型在Spring中是如何访问的呢?Spring提供了ResourceLoader接口用于实现不同的Resource加载策略,该接口的实例对象中可以获取一个resource对象。如图3所示,在ResourceLoader接口中只定义了两个方法:
在这里插入图片描述
图3 ResourceLoader接口中的两个方法
注:ApplicationContext的所有实现类都实现RecourceLoader接口,因此可以直接调用getResource(参数)获取Resoure对象。不同的ApplicatonContext实现类使用getResource方法取得的资源类型不同,例如:FileSystemXmlApplicationContext.getResource获取的就是FileSystemResource实例;ClassPathXmlApplicationContext.getResource获取的就是ClassPathResource实例;
XmlWebApplicationContext.getResource获取的就是ServletContextResource实例,另外像不需要通过xml直接使用注解@Configuation方式加载资源的AnnotationConfigApplicationContext等等。
在资源定位过程完成以后,就为资源文件中的bean的载入创造了I/O操作的条件,如何读取资源中的数据将会在下一步介绍的BeanDefinition的载入过程中描述。

4、载入BeanDefinition
BeanDefinition是一个数据结构,BeanDefinition是根据resource对象中的bean来生成的。bean会在Spring IoC容器内部以BeanDefintion的形式存在,IoC容器对bean的管理和依赖注入的实现是通过操作BeanDefinition来完成的。BeanDefinition就是Bean在IoC容器中的存在形式。
由于Spring的配置文件主要是XML格式,一般而言会使用到AbstractXmlApplicationContext类进行文件的读取,如图4所示,该类定义了一个名为loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 的方法用于获取BeanDefinition。
方法体内会new一个BeanDefinitionReader对象,然后将生成的实例传入loadBeanDefintions方法。
在这里插入图片描述
图4 AbstractXmlApplicationContext
接下来以XmlBeanDefinitionReader对象载入BeanDefinition为例,如图5所示,调用loadBeanDefinitions方法传入对象,分别加载configResources(定位到的resource资源位置)和configLocation(本地配置文件的位置)。也就是将用户定义的资源以及容器本身需要的资源全部加载到reader中。
在这里插入图片描述
图5 loadBeanDefinitions方法
顺着看reader中的loadBeanDefinitions方法,该方法override了AbstractBeanDefinitionReader类,父接口的BeanDefinitionReader。方法体中,将所有资源全部加载,并且交给AbstractBeanDefinitionReader的实现子类处理这些resource。
在这里插入图片描述
图6 reader 中的loadBeanDefinitions
如图7所示,BeanDefinitionReader接口定义了 int loadBeanDefinitions(Resource resource)方法。
在这里插入图片描述
图7 BeanDefinitionReader接口定义的方法。
此时回到XmlBeanDefinitionReader上来,它主要针对XML方式的Bean进行读取,XmlBeanDefinitionReader主要是实现了AbstractBeanDefinitionReader抽象类,而该类继承与BeanDefinitionReader,主要实现的方法也是来自于BeanDefinitionReader 的loadBeanDefintions(Resource)方法。
在这里插入图片描述
图8 XmlBeanDefinitionReader 继承关系图
如图9所示,读取Bean之后就是加载Bean的过程了,XmlBeanDefinitionReader中的doLoadBeanDefinitions方法主要来处理加载Bean的工作。首先对资源进行验证,然后从资源对象中加载Document对象,使用了documentLoader中的loadDocument方法,然后跟上registerBeanDefinitions对文档对应的resource进行注册,也就是将XML文件中的Bean转换成容器中的BeanDefinition。
在这里插入图片描述
图9 doLoadBeanDefinitions方法
如图10所示,接下来就是registerBeanDefinitions方法了,它主要对Spring Bean语义进行转化,变成BeanDefintion类型。首先获取DefaultBeanDefinitionDocumentReader实例,然后获取容器中的bean数量,通过documentReader中的registerBeanDefinitions方法进行注册和转化工作。
在这里插入图片描述
图10 registerBeanDefintions
顺着上面的思路继续往下,在DefaultBeanDefinitionDocumentReader 中的registerBeanDefinitions 方法如图11所示,其获取document的根结点然后顺势访问所有的子节点。同时把处理BeanDefinition的过程委托给BeanDefinitionParserDelegate对象来完成。
在这里插入图片描述
图11 DefaultBeanDefinitionDocumentReader 中的registerBeanDefinitions 方法
BeanDefinitionParserDelegate类主要负责BeanDefinition的解析,这里涉及到JDK和CGLIB动态代理的知识,这里留一个悬念我们后面的章节会深入介绍。BeanDefinitionParserDelegate代理类会完成对符合Spring Bean语义规则的处理,比如、、等的检测。如图12 所示,就是BeanDefinitionParserDelegate代理类中的parseBeanDefinitions方法,用来对XML文件中的节点进行解析。通过遍历import标签节点调用importBeanDefinitionResource方法对其进行处理,然后接着便利bean节点调用processBeanDefinition对其处理。
在这里插入图片描述
图12 parseBeanDefinitions 方法
如图13 再看parseBeanDefinitions方法中调用的parseDefaultElement方法,顾名思义它是对节点元素进行处理的。从方法体的语句可以看出它对import标签、alias标签、bean标签进行了处理。每类标签对应不同的BeanDefinition的处理方法。
在这里插入图片描述
图13 parseDefaultElement 方法
在parseDefaultElement调用的众多方法中,我们选取processBeanDefinition方法给大家讲解,如图14 所示,该方法是用来处理Bean的。首先通过delegate的parseBeanDefinitionElement方法传入节点信息,获取该Bean对应的name和alias。然后通过BeanDefinitionReaderUtils中的registerBeanDefinition方法对其进行容器注册,也就是将Bean实例注册到容器中进行管理。最后,发送注册事件。
在这里插入图片描述
图14 processBeanDefinition 方法
至此完成了BeanDefinition的加载工作。

5、将BeanDefiniton注册到容器
在加载了Bean之后,就需要将其注册到容器中尽心管理。如图15所示,Bean会被解析成BeanDefinition并与BeanName、Alias一同封装到BeanDefinitionHolder类中, 之后beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()),注册到DefaultListableBeanFactory.beanDefinitionMap中。如果客户端需要获取Bean对象,Spring容器会根据注册的BeanDefinition信息进行实例化。
在这里插入图片描述
图15 registerBeanDefinition
DefaultListableBeanFactory实现了上面调用BeanDefinitionRegistry接口的 registerBeanDefinition( beanName, bdHolder.getBeanDefinition())方法。如图16所示,这一部分的主要逻辑是向DefaultListableBeanFactory对象的beanDefinitionMap中存放beanDefinition,也就是说beanDefinition都放在beanDefinitionMap中进行管理。当初始化容器进行bean初始化时,在bean的生命周期分析里必然会在这个beanDefinitionMap中获取beanDefition实例。
在这里插入图片描述
图16 registerBeanDefinition方法
6、总结
主要介绍了Spring IOC容器初始化的过程,包括Resource定位:通过文件路径、类路径、web路径等方式获取Bean信息;载入BeanDefinition:介绍的是如何将Bean载入到IoC中形成BeanDefinition的整个过程;将BeanDefinition注册到容器。

热门文章

暂无图片
编程学习 ·

C语言二分查找详解

二分查找是一种知名度很高的查找算法,在对有序数列进行查找时效率远高于传统的顺序查找。 下面这张动图对比了二者的效率差距。 二分查找的基本思想就是通过把目标数和当前数列的中间数进行比较,从而确定目标数是在中间数的左边还是右边,将查…
暂无图片
编程学习 ·

GMX 命令分类列表

建模和计算操作命令: 1.1 . 创建拓扑与坐标文件 gmx editconf - 编辑模拟盒子以及写入子组(subgroups) gmx protonate - 结构质子化 gmx x2top - 根据坐标生成原始拓扑文件 gmx solvate - 体系溶剂化 gmx insert-molecules - 将分子插入已有空位 gmx genconf - 增加…
暂无图片
编程学习 ·

一文高效回顾研究生课程《数值分析》重点

数值分析这门课的本质就是用离散的已知点去估计整体,就是由黑盒子产生的结果去估计这个黑盒子。在数学里这个黑盒子就是一个函数嘛,这门课会介绍许多方法去利用离散点最大化地逼近这个函数,甚至它的导数、积分,甚至微分方程的解。…
暂无图片
编程学习 ·

在职阿里5年,一个28岁女软测工程师的心声

简单的先说一下,坐标杭州,14届本科毕业,算上年前在阿里巴巴的面试,一共有面试了有6家公司(因为不想请假,因此只是每个晚上去其他公司面试,所以面试的公司比较少) ​ 编辑切换为居中…
暂无图片
编程学习 ·

字符串左旋c语言

目录 题目: 解题思路: 第一步: 第二步: 第三步: 总代码: 题目: 实现一个函数,可以左旋字符串中的k个字符。 例如: ABCD左旋一个字符得到BCDA ABCD左旋两个字符…
暂无图片
编程学习 ·

设计模式--观察者模式笔记

模式的定义与特点 观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式&#xf…
暂无图片
编程学习 ·

睡觉突然身体动不了,什么是睡眠痽痪症

很多朋友可能有这样的体验,睡觉过程中突然意识清醒,身体却动弹不了。这时候感觉非常恐怖,希望旁边有一个人推自己一下。阳光以前也经常会碰到这样的情况,一年有一百多次,那时候很害怕晚上到来,睡觉了就会出…
暂无图片
编程学习 ·

深入理解C++智能指针——浅析MSVC源码

文章目录unique_ptrshared_ptr 与 weak_ptrstd::bad_weak_ptr 异常std::enable_shared_from_thisunique_ptr unique_ptr 是一个只移型别(move-only type,只移型别还有std::mutex等)。 结合一下工厂模式,看看其基本用法&#xff…
暂无图片
编程学习 ·

@TableField(exist = false)

TableField(exist false) //申明此字段不在数据库存在,但代码中需要用到它,通知Mybatis-plus在做写库操作是忽略它。,.
暂无图片
编程学习 ·

Java Web day15

第十二章文件上传和下载 一、如何实现文件上传 要实现Web开发中的文件上传功能,通常需要完成两步操作:一.是在Web页面中添加上传输入项;二是在Servlet中读取上传文件的数据,并保存到本地硬盘中。 需要使用一个Apache组织提供一个…
暂无图片
编程学习 ·

【51nod 2478】【单调栈】【前缀和】小b接水

小b接水题目解题思路Code51nod 2478 小b接水 题目 输入样例 12 0 1 0 2 1 0 1 3 2 1 2 1输出样例 6解题思路 可以发现最后能拦住水的都是向两边递减高度(?) 不管两个高积木之间的的积木是怎样乱七八糟的高度,最后能用来装水的…
暂无图片
编程学习 ·

花了大半天写了一个UVC扩展单元调试工具

基于DIRECTSHOW 实现的,用的是MFC VS2019. 详见:http://www.usbzh.com/article/detail-761.html 获取方法 加QQ群:952873936,然后在群文件\USB调试工具&测试软件\UVCXU-V1.0(UVC扩展单元调试工具-USB中文网官方版).exe USB中文网 USB中文…
暂无图片
编程学习 ·

贪心(一):区间问题、Huffman树

区间问题 例题一:区间选点 给定 N 个闭区间 [ai,bi]请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入格式 第一行包含整数 N,表示区间数。 接下来 …
暂无图片
编程学习 ·

C语言练习实例——费氏数列

目录 题目 解法 输出结果 题目 Fibonacci为1200年代的欧洲数学家,在他的着作中曾经提到:「若有一只免子每个月生一只小免子,一个月后小免子也开始生产。起初只有一只免子,一个月后就有两只免子,二个月后有三只免子…
暂无图片
编程学习 ·

Android开发(2): Android 资源

个人笔记整理 Android 资源 Android中的资源,一般分为两类: 系统内置资源:Android SDK中所提供的已经定义好的资源,用户可以直接拿来使用。 用户自定义资源:用户自己定义或引入的,只适用于当前应用的资源…
暂无图片
编程学习 ·

零基础如何在短时间内拿到算法offer

​算法工程师是利用算法处理事物的职业 算法(Algorithm)是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。 如果一个算法有缺陷,或不适合于某个问题,执…
暂无图片
编程学习 ·

人工智能:知识图谱实战总结

人工智能python,NLP,知识图谱,机器学习,深度学习人工智能:知识图谱实战前言一、实体建模工具Protegepython,NLP,知识图谱,机器学习,深度学习 人工智能:知识图…
暂无图片
编程学习 ·

【无标题】

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…