Spring IoC之旅(二)

一、IoC容器的初始化过程

1、IoC容器的流程

前面已经分析过IoC容器的整体流程。

  • 第一阶段(载入阶段)

    • 寻址:寻找配置文件地址并转成Resource。

      Resource的定位指的是BeanDefinition的定位,也就是需要被注册成bean的资源的定位,比如在XML中进行bean的配置的资源定位。 Resource的定位通过ResourceLoader进行定位。不同的Resource可以由不同的ResourceLoader的不同实现进行加载。

      如果你有一个容纳金币的金库,需要将进行寻找钱(配置文件),不同的钱的寻找方式不同,需要去不同的国家获取。只要是想要把容器填满,就需要进行找到根源。

    • 加载:将Resource进行加载,解析成BeanFactory容器的bean的数据结构BeanDefinition

      加载阶段指的是将Resource文件里面的bean信息表示成IoC统一管理的数据格式,而这种数据格式就是BeanDefinition。BeanDefinition实际上就是将Bean信息进行抽象的数据结构。

      当你有人民币、美元等等钱找到了之后,为了方便容器管理,你需要将不同的货币转换成一个统一的管理方式,就是金币(BeanDefinition)。

    • 注册:将BeanDefinition进行注册到IoC容器中

      将BeanDefinition进行注册,存放在一个HashMap中。

  • 第二阶段(依赖注入阶段)

    从上面的第一阶段,载入阶段已经完成了用户配置的bean信息被存放在IoC容器中。那么接下来就是一个将容器中的bean信息转换为实际需要的Object了。
    依赖注入一般发生在首次getBean()的操作发生的时候,由于bean的层层依赖,所以注入的方式也是使用递归的方式进行依赖注入。

二、IoC容器载入阶段

1、寻址阶段

在寻址阶段,先看一下ApplicationContext的各种实现,可以看到实现有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等等多种实现,顾名思义就是不同的加载Resource的方式。

写起来太复杂了 直接上图吧
IoC容器寻址

2、加载阶段

2.1 加载阶段

为了方便起见,选择最常用的ApplicationContext的实现类FileSystemXmlApplicationContext进行分析。先看FileSystemXmlApplicationContext的构造函数,可以看到构造函数的定义如下:
构造函数

从上面的代码可以看到,FileSystemXmlApplicationContext是表示可以指定的XML配置文件中进行加载。在构造函数中调用了refresh()函数,该函数是加载Bean的主要入口。先来看一下方法的具体实现:

s2

再跟进来obtainFreshBeanFactory()方法的具体实现:

s3

在refreshBeanFactory方法中可以看到它的具体实现是在AbstractRefreshableApplicationContext中,而AbstractRefreshableApplicationContext又是继承自AbstractApplicationContext,如下图:

s5

可以看到refreshBeanFactory先创建了一个DefaultlistBeanFactory做为容器,调用了一个loadBeanDefinitions的方法进行bean的加载。再来看一下loadBeanDefinitions方法的实现。
s5

s6

整理一下整体的流程思路:

  • 首先通过refresh()方法触发IoC容器的加载过程。
  • 如果IoC容器已经创建就销毁(类似机器重启),然后重新创建一个DefaultListableBeanFactory作为容器。
  • 使用XmlBeanDefinitionReader与DefaultListableBeanFactory进行关联并加载beandefinitions。
2.2 如何加载BeanDefinition

既然已经知道了整体的流程,那么配置文件是如何转换成IoC容器中的BeanDefinition的呢?

顺着上面的最后一步,跟进来看到loadBeanDefinitions的具体实现:

s7

从源代码可以发现loadBeanDefinitions实际上调用了doLoadBeanDefinitions方法:
s8

继续往下看:
s9

从上面可以看到将Resource的内容解析成BeanDefinition主要分两步,第一步是先把Resource转换成待会可被解析的Document,第二步是将XML文件中的bean信息进行获取并将其构建到BeanDefinitionHolder中并且生成BeanDefinition。

3、注册阶段

在2中已经生成了BeanDefinition,那么是它如何放置在容器中,就是IoC的注册阶段了。

  • 生成之后是如何放入IoC容器中,也就是上文生成的DefaultListableBeanFactory中?

从上面可以看到,注册的最后一步的代码是:

s11
在代码中使用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());其实这里的getRegistry()返回的就是XmlReaderContext的BeanDefinitionRegistry。

s12

而在2中创建的DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,而这里的BeanDefinitionRegistry就是DefaultListableBeanFactory的BeanDefinitionRegistry,而具体实现代码是在DefaultListableBeanFactory类中,具体实现如下:
s13
s14
在这里可以确认这里的registry就是DefaultListableBeanFactory,而调用注册的方法registerBeanDefinition()就是DefaultListableBeanFactory自己实现的,可以在DefaultListableBeanFactory找到该方法:
s16
可以看到,它实际上是将BeanDefinition放入一个beanDefinitionMap里面,将bean的name做key,value是BeanDefinition的方式进行存储。存储的Map实际上就是DefaultListableBeanFactory的一个成员变量,定义如下:
s16

坚持原创技术分享,您的支持将鼓励我继续创作!