从源码看Spring中IOC容器的实现(二):IOC容器的初始化

初始化的三个主要过程

Spring中IOC容器的初始化是由refresh()方法来启动的,该方法标志着IOC容器的正式启动。具体来讲,这个启动包括BeanDefinition的Resource定位、载入和注册三个基本过程。数据结构

注意:这里的初始化不包含Bean依赖注入的实现dom

Spring将上述三个过程分离,使用相应的ResourceLoader、BeanDefinitionReader等模块,这样设计可让用户更加灵活地对这三个过程进行剪裁或扩展,函数

  1. Resoure定位:即BeanDefinition的资源定位,由ResourceLoader经过统一的Resource接口来完成,Resource对各类形式的BeanDefinition的使用都提供了统一接口。
  2. BeanDefinition的载入:这个载入过程是把用户定义好的Bean表示成IOC容器内部的数据结构,即BeanDefinition。
  3. BeanDefinition的注册:经过调用BeanDefinitionRegistry接口的实现来完成。这个注册过程把载入过程当中解析获得的BeanDefinition向IOC容器中注册,实际是注入到一个HashMap中。

BeanDefinition:

上一篇文章咱们介绍了Spring提供的基本IOC容器的接口定义和实现.在这些Spring提供的基本IOC容器的接口定义和实现的基础上,Spring经过定义BeanDefinition来管理基于Spring的应用中的各类对象以及他们之间的相互依赖关系。设计

BeanDefinition抽象了咱们对Bean的定义,是让容器起做用的主要数据类型。对IOC容器来讲,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。3d

Resource定位

以FileSystemXmlApplicationContext为例。其经过getResourceByPath方法加载xml,返回一个Resource。这里使用了模板方法模式。cdn

在FileResourceLoader中能够找到一样的方法,而该方法来自于FileResourceLoader重写其父类DefaultResourceLoader,该方法在ResourceLoader接口中定义,在FileResourceLoader和DefaultResourceLoader中都提供了各自的实现。而其调用在DefaultResourceLoader的getResource方法中:xml

这里确实是模板方法模式,子类覆盖父类实现。而经过查找该方法的调用,首先是看FileSystemXmlApplicationContext的loadBeanDefinitions方法,该方法在AbstractXmlApplicationContext中实现:对象

此时已经在AbstractXmlApplicationContext中注入了对应的BeanDefinitionReader,再调用reader的loadBeanDefinitions方法,追进去看AbstractBeanDefinitionReader类,在这里实现了:blog

这里能够看出在BeanDefinitionReader中注入了一个ResourceLoader,此处能够看到对ResourceLoader调用了getResource方法,这样就能够完成对该方法的整个追溯过程了索引

要注意到FileSystemXmlApplicationContext自己就是一个DefaultResourceLoader的子类,其实FileSystemXmlApplicationContext中的getResource就是给父类做为模板方法调用。

那么FileSystemXmlApplicationContext是在什么地方定义了BeanDefinition的读入器BeanDefinitionReader并完成BeanDefinition的读入呢?

BeanDefinition的载入

先回到IOC容器的初始化入口,也就是看一下refresh方法,这个方法的最初是在FileSystemXmlApplicationContext的构造函数中被调用的,它的调用标志着容器初始化的开始,这些初始化对象就是BeanDefinition数据。

对容器的启动来讲,refresh是一个很重要的方法。该方法在AbstractApplicationContext类中找到,它详细的描述了整个ApplicationContext的初始化过程。

咱们去看FileSystemXmlApplicationContext的基类AbstractRefreshableApplicationContext是怎样实现的。 首先看他的refreshBeanFactory方法:

首先建立其须要注入的DefaultListableBeanFactory,接着调用loadBeanDefinitions来载入资源进入其所持有的BeanFactory,而这个方法在该类中是一个空实现,留给子类去作对应的实现。 而在AbstractXMLApplicationContext中,咱们找到了对应的具体实现:

这里能够清楚的看到读入器的建立和配置,这里AbstractXMLApplicationContext将DefaultListableBeanFactory做为参数注入了BeanDefinitionReader,但注意,其注入是以BeanDefinitionRegistry接口的形式:

父类AbstractBeanDefinitionReader构造器:

若是传入的BeanFactory只实现了BeanDefinitionRegistry,那就注入,若是还实现了ResourceLoader,就把它做为默认的ResourceLoader,若是实现了EnvironmentCapable,会把其内部的环境也加载进来。 而后咱们回过来继续看其子类XMLBeanDefinitionReader,其重载了loadBeanDefinitions方法,不使用父类的实现

这里调用了doLoadBeanDefinition方法:

该方法一共就两步,一步是加载dom,另外一步是从dom将BeanDefinition注册到BeanFactory中。

在注册的方法中,是经过调用BeanDefinitionDocumentReader进行的,实际调用的是其实现类DefaultBeanDefinitionDocumentReader,在其方法中又是经过其持有的BeanDefinitionParserDelegate进行的。

总结一下流程:能够看到,在初始化FileSystemXMLApplicationContext的过程当中是经过调用IOC容器的refresh来启动整个BeanDefinition的载入过程的,这个初始化是经过定义的XMLBeanDefinitionReader来完成。同时,咱们也知道实际使用的IOC容器是DefaultListableBeanFactory,具体的Resource载入在XMLBeanDefinitionReader读入BeanDefinition时实现。在XMLBeanDefinitionReader的实现中能够看到,是在reader.loadBeanDefinition中开始进行BeanDefinition的载入,而这时其父类AbstractBeanDefinitionReader已经为BeanDefinition的载入作好了准备。

这里调用的是loadBeanDefinitions(Resource res)方法,但这个方法在该类中是没有实现的,具体实如今XMLBeanDefinitionReader中。在读取器中,须要获得表明 xml文件的Resource,由于这个Resource对象封装了对xml文件的I/O操做,因此读取器能够在打开IO流后获得xml的文件对象,有了这个文件对象后,就能够按照Spring的Bean定义规则来对这个xml的文档树进行解析。

BeanDefinition的载入分红两部分,首先经过调用xml的解析器获得document对象,但这些对象并无按照Spring的Bean规则进行解析。在完成通用的xml解析之后,才是按照Spring的Bean规则进行解析的地方,这个按照Spring的Bean规则进行解析的过程是在DocumentReader中实现的。这里使用的是默认的DefaultBeanDefinitionDocumentReader。这个DefaultBeanDefinitionDocumentReader的建立是在后面的方法中完成的,而后再完成BeanDefinition的处理,处理的结果由BeanDefinitionHolder对象来持有。这个BeanDefinitionHolder除了持有BeanDefinition对象外,还持有其余与BeanDefinition的使用相关的信息,好比Bean的名字、别名集合等。这个BeanDefinitionHolder的生成是经过对Document文档树的内容解析来完成的,能够看到这个解析过程是由BeanDefinitionParserDelegate来实现(具体在上面的processBeanDefinition方法中)。

解析过程: 调用delegate的parseBeanDefinitionElement方法:

在高亮的这一行进行对bean元素的详细解析:

再往里面就是各类元素的解析了,太细了之后慢慢看。

通过这样逐层的解析,咱们在xml文件中定义的BeanDefinition就被整个载入到了IOC容器中,并在容器中创建了数据映射。在IOC容器中创建了对应的数据结构,或者说能够当作是pojo对象在IOC容器中的抽象,这些数据结构能够以AbstractBeanDefinition为入口,让IOC容器执行索引、查询和操做。通过以上过程,IOC容器大体完成了管理Bean对象的数据准备工做。可是,重要的依赖注入实际上在这个时候还没发生。如今IOC容器BeanDefinition中存在的还只是一些静态的配置信息。严格的说这时候容器尚未彻底起做用,要彻底发挥容器的做用,还需完成数据向容器的注册。

BeanDefinition的注册

在BeanDefinition的载入和解析完成后,用户定义的BeanDefinition信息已经在IOC容器内创建了本身的数据结构以及相应的数据表示,但此时这些数据还不能供IOC容器直接使用,须要在IOC中对这些BeanDefinition数据进行注册。在DefaultListableBeanFactory中,是经过一个HashMap来持有载入的BeanDefinition的,这个HashMap的定义在DefaultListableBeanFactory中能够看到。

将解析获得的BeanDefinition向IOC容器中的map注册的过程是在载入BeanDefinition完成后进行的,调用过程以下:

具体实现: DefaultBeanDefinitionDocumentReader中:

BeanDefinitionReaderUtils中:

调用了注入的registry(即DefaultListableBeanFactory): 看DefaultListableBeanFactory中的registerBeanDefinition方法:

到这里注册就已经完成了。

相关文章
相关标签/搜索