IOC容器的初始化(二)

继续上一节降到解析到<alias>元素之后的内容


(12)BeanDefinitionParserDelegate 解析 Bean 定义资源文件中的<bean>元素:

Bean 定义资源文件中的<import><alias>元素解析在 DefaultBeanDefinitionDocumentReader中已经完成, 对 Bean 定义资源文件中使用最多的<bean>元素交由 BeanDefinitionParserDelegate来解析, 其解析实现的源码如下:


只要使用过Spring,对Spring配置文件比较熟悉的人,通过对上述源码的分析,就会明白我们在Spring配置文件中<Bean>元素的中配置的属性就是通过该方法解析和设置到Bean中去的。
注意:在解析<Bean>元素过程中没有创建和实例化Bean对象,只是创建了Bean对象的定义类BeanDefinition,将<Bean>元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。

上面方法中一些对一些配置如元信息(meta)、qualifier等的解析,我们在Spring中配置时使用的也不多,我们在使用Spring的<Bean>元素时,配置最多的是<property>属性,因此我们下面继续分析源码,了解Bean的属性在解析时是如何设置的。

(13)、BeanDefinitionParserDelegate解析<property>元素:BeanDefinitionParserDelegate在解析<Bean>调用parsePropertyElements方法解析<Bean>元素中的<property>属性子元素,解析源码如下:


通过对上述源码的分析,我们可以了解在Spring配置文件中,<Bean>元素中<property>元素的相关

配置是如何处理的:
a.ref被封装为指向依赖对象一个引用。
b.value配置都会封装成一个字符串类型的对象。
c.ref和value都通过解析的数据类型属性值.setSource(extractSource(ele))方法将属性值/引用与所引用的属性关联起来。

在方法的最后对于<property>元素的子元素通过parsePropertySubElement方法解析,继续分析该方法的源码,了解其解析过程。

(14)、 解析<property>元素的子元素:
BeanDefinitionParserDelegate类中的parsePropertySubElement方法对<property>中的子元素解析,源码如下:

通过上述源码分析,我们明白了在Spring配置文件中,对<property>元素中配置的array、list、set、map、prop等各种集合子元素的都通过上述方法解析,生成对应的数据对象,比如ManagedList、ManagedArray、ManagedSet等,这些Managed类是Spring对象BeanDefiniton的数据封装,对集合数据类型的具体解析有各自的解析方法实现,解析方法的命名非常规范,一目了然,我们对<list>集合元素的解析方法进行源码分析,了解其实现过程。

(15)、解析<list>子元素:
在BeanDefinitionParserDelegate类中的parseListElement方法就是具体实现解析<property>

元素中的<list>集合子元素,源码如下:

经过对SpringBean定义资源文件转换的Document对象中的元素解析,SpringIOC现在已经将XML形式定义的Bean定义资源文件转换为SpringIOC所识别的数据结构——BeanDefinition,它是Bean定义资源文件中配置的POJO对象在SpringIOC容器中的映射,我们可以通过AbstractBeanDefinition为入口,看到了IOC容器进行索引、查询和操作。通过SpringIOC容器对Bean定义资源的解析后,IOC容器大致完成了管理Bean对象的准备工作,即初始化过程,但是最为重要的依赖注入还没有发生,现在在IOC容器中BeanDefinition存储的只是一些静态信息,接下来需要向容器注册Bean定义信息才能全部完成IOC容器的初始化过程。
(16)、解析过后的BeanDefinition在IOC容器中的注册:
让我们继续跟踪程序的执行顺序,接下来我们来分析DefaultBeanDefinitionDocumentReader对Bean定义转换的Document对象解析的流程中,在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象,然后调用BeanDefinitionReaderUtils的registerBeanDefinition方法向IOC容器注册解析的Bean,

BeanDefinitionReaderUtils的注册的源码如下:


当调用 BeanDefinitionReaderUtilsIOC容器注册解析的 BeanDefinition 时,完成注册功能的是 DefaultListableBeanFactory

(17)DefaultListableBeanFactory IOC 容器注册解析后的 BeanDefinition
DefaultListableBeanFactory 中 使 用 一 个 HashMap 的 集 合 对 象 存 放 IOC 容 器 中 注 册 解 析 的
BeanDefinition , 向 IOC 容器注册的主要源码如下:

至此,Bean定义资源文件中配置的Bean被解析过后,已经注册到IOC容器中,被容器管理起来,真正完成了IOC容器初始化所做的全部工作。现在IOC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并且可以被检索,IOC容器的作用就是对这些注册的Bean定义信息进行处理和维护。这些的注册的Bean定义信息是IOC容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。
总结:
现在通过上面的代码,总结一下IOC容器初始化的基本步骤:
(1).初始化的入口在容器实现中的refresh()调用来完成。
(2).对bean定义载入IOC容器使用的方法是loadBeanDefinition,
其中的大致过程如下:通过ResourceLoader来完成资源文件位置的定位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了ResourceLoader的实现,可以从类路径,文件系统,URL等方式来定为资源位置。如果是XmlBeanFactory作为IOC容器,那么需要为它指定bean定义的资源,
也就是说bean定义文件时通过抽象成Resource来被IOC容器处理的,容器通过BeanDefinitionReader来完成定义信息的解析和Bean信息的注册,往往使用的是XmlBeanDefinitionReader来解析bean的xml定义文件-实际的处理过程是委托给BeanDefinitionParserDelegate来完成的,从而得到bean的定义信息,这些信息在Spring中使用BeanDefinition对象来表示-这个名字可以让我们想到loadBeanDefinition,RegisterBeanDefinition这些相关方法-他们都是为处理BeanDefinitin服务的,容器解析得到BeanDefinition以后,需要把它在IOC容器中注册,这由BeanDefinitionRegistry接口来实现。注册过程就是在IOC容器内部维护的一个HashMap来保存得到的BeanDefinition的过程。这个HashMap是IOC容器持有Bean信息的场所,以后对Bean的操作都是围绕这个HashMap来实现的。然后我们就可以通过BeanFactory和ApplicationContext来享受到SpringIOC的服务了,在使用IOC容器的时候,除了少量粘合代码,绝大多数以正确IOC风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。Spring本身提供了对声明式载入web应用程序用法的应用程序上下文,并将其存储在ServletContext中的框架实现。

以下是容器初始化全过程的时序图:


在使用SpringIOC容器的时候我们还需要区别两个概念:BeanFactory和FactoryBean,其中BeanFactory指的是IOC容器的编程抽象,比如ApplicationContext,XmlBeanFactory等,这些都是IOC容器的具体表现,需要使用什么样的容器由客户决定,但Spring为我们提供了丰富的选择。FactoryBean只是一个可以在IOC而容器中被管理的一个Bean,是对各种处理过程和资源使用的抽象,FactoryBean在需要时产生另一个对象,而不返回FactoryBean本身,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的FactoryBean都实现特殊的org.springframework.beans.factory.FactoryBean接口,当使用容器中FactoryBean的时候,该容器不会返回FactoryBean本身,而是返回其生成的对象。Spring包括了大部分的通用资源和服务访问抽象的FactoryBean的实现,其中包括:对JNDI查询的处理,对代理对象的处理,对事务性代理的处理,对RMI代理的处理等,这些我们都可以看成是具体的工厂,看成是Spring为我们建立好的工厂。也就是说Spring通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在IOC容器里配置好就能很方便的使用了。

上一篇:Spring IOC容器的初始化(一)

下一篇:Spring 自定义标签使用

猜你喜欢

转载自blog.csdn.net/m0_37444820/article/details/80791547