IOC 容器的初始化过程分为三步骤:java
- Resource 定位
- BeanDefinition 的载入和解析
- BeanDefinition 注册

- Resource 定位。咱们通常用外部资源来描述 Bean 对象,因此在初始化 IOC 容器的第一步就是须要定位这个外部资源。
- BeanDefinition 的载入和解析。装载就是 BeanDefinition 的载入。BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IOC 容器的内部数据结构:BeanDefinition。在 IOC 容器内部维护着一个 BeanDefinition Map 的数据结构,在配置文件中每个都对应着一个BeanDefinition对象。
- BeanDefinition 注册。向IOC容器注册在第二步解析好的 BeanDefinition,这个过程是经过 BeanDefinitionRegistery 接口来实现的。在 IOC 容器内部实际上是将第二个过程解析获得的 BeanDefinition 注入到一个 HashMap 容器中,IOC 容器就是经过这个 HashMap 来维护这些 BeanDefinition 的。在这里须要注意的一点是这个过程并无完成依赖注入,依赖注册是发生在应用第一次调用 getBean() 向容器索要 Bean 时。固然咱们能够经过设置预处理,即对某个 Bean 设置 lazyinit 属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。
// 根据 Xml 配置文件建立 Resource 资源对象。ClassPathResource 是 Resource 接口的子类,
//bean.xml 文件中的内容是咱们定义的 Bean 信息。
ClassPathResource resource = new ClassPathResource("bean.xml");
//建立一个 BeanFactory。DefaultListableBeanFactory 是 BeanFactory 的一个子类,BeanFactory //做为一个接口,其实它自己是不具备独立使用的功能的,而 DefaultListableBeanFactory 则是真正能够独立
//使用的 IOC 容器,它是整个 Spring IOC 的始祖,在后续会有专门的文章来分析它。
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//建立 XmlBeanDefinitionReader 读取器,用于载入 BeanDefinition 。
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//开启 Bean 的载入和注册进程,完成后的 Bean 放置在 IOC 容器中。
reader.loadBeanDefinitions(resource);
Resource 定位
- Spring 为了解决资源定位的问题,提供了两个接口:Resource、ResourceLoader
- 其中 Resource 接口是 Spring 统一资源的抽象接口
- ResourceLoader 则是 Spring 资源加载的统一抽象。
- 在上面那段代码中
new ClassPathResource("bean.xml")
为咱们定义了资源
- 那么 ResourceLoader 则是在何时初始化的呢?
看 XmlBeanDefinitionReader 构造方法:数据结构

直接调用父类 AbstractBeanDefinitionReader :post

- 核心在于设置 resourceLoader 这段,若是设置了 ResourceLoader 则用设置的,
- 不然使用 PathMatchingResourcePatternResolver ,该类是一个集大成者的 ResourceLoader。
BeanDefinition 的载入和解析
reader.loadBeanDefinitions(resource);
开启 BeanDefinition 的解析过程。以下:this

- 而将 Resource 封装成 EncodedResource 主要是为了对 Resource 进行编码,保证内容读取的正确性

- 从 encodedResource 源中获取 xml 的解析源,调用
doLoadBeanDefinitions()
执行具体的解析过程
- 在该方法中主要作两件事:
- 一、根据 xml 解析源获取相应的 Document 对象,
- 二、调用
registerBeanDefinitions()
开启 BeanDefinition 的解析注册过程。

转换为 Document 对象
- 调用
doLoadDocument()
会将 Bean 定义的资源转换为 Document 对象。

- 就已经将定义的 Bean 资源文件,载入并转换为 Document 对象了,那么下一步就是如何将其解析为 Spring IOC 管理的 Bean 对象并将其注册到容器中。
- 这个过程有方法
registerBeanDefinitions()
实现。以下:

- 首先建立 BeanDefinition 的解析器 BeanDefinitionDocumentReader,
- 而后调用 documentReader.registerBeanDefinitions() 开启解析过程,这里使用的是委派模式,具体的实现由子类 DefaultBeanDefinitionDocumentReader 完成。

对 Document 对象的解析
- 从 Document 对象中获取根元素 root,而后调用
doRegisterBeanDefinitions()
开启真正的解析过程

preProcessXml()
、postProcessXml()
为前置、后置加强处理,目前 Spring 中都是空实现,
-
parseBeanDefinitions()
是对根元素 root 的解析注册过程

- 迭代 root 元素的全部子节点,对其进行判断,若节点为默认命名空间,则ID调用
parseDefaultElement()
开启默认标签的解析注册过程,
- 不然调用
parseCustomElement()
开启自定义标签的解析注册过程。
标签解析
- 若定义的元素节点使用的是 Spring 默认命名空间,则调用
parseDefaultElement()
进行默认标签解析,以下:

- 对四大标签:import、alias、bean、beans 进行解析,其中 bean 标签的解析为核心工做
对于默认标签则由 parseCustomElement()
负责解析。

注册 BeanDefinition

- 调用 BeanDefinitionReaderUtils.registerBeanDefinition() 注册,
- 其实这里面也是调用 BeanDefinitionRegistry 的 registerBeanDefinition()来注册 BeanDefinition ,
- 不过最终的实现是在 DefaultListableBeanFactory 中实现,以下:
- 最核心的部分是这句 this.beanDefinitionMap.put(beanName, beanDefinition) ,
- 因此注册过程也不是那么的高大上,就是利用一个 Map 的集合对象来存放,key 是 beanName,value 是 BeanDefinition。
