一、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的方式。
写起来太复杂了 直接上图吧
2、加载阶段
2.1 加载阶段
为了方便起见,选择最常用的ApplicationContext的实现类FileSystemXmlApplicationContext进行分析。先看FileSystemXmlApplicationContext的构造函数,可以看到构造函数的定义如下:
从上面的代码可以看到,FileSystemXmlApplicationContext是表示可以指定的XML配置文件中进行加载。在构造函数中调用了refresh()函数,该函数是加载Bean的主要入口。先来看一下方法的具体实现:
再跟进来obtainFreshBeanFactory()方法的具体实现:
在refreshBeanFactory方法中可以看到它的具体实现是在AbstractRefreshableApplicationContext中,而AbstractRefreshableApplicationContext又是继承自AbstractApplicationContext,如下图:
可以看到refreshBeanFactory先创建了一个DefaultlistBeanFactory做为容器,调用了一个loadBeanDefinitions的方法进行bean的加载。再来看一下loadBeanDefinitions方法的实现。
整理一下整体的流程思路:
- 首先通过refresh()方法触发IoC容器的加载过程。
- 如果IoC容器已经创建就销毁(类似机器重启),然后重新创建一个DefaultListableBeanFactory作为容器。
- 使用XmlBeanDefinitionReader与DefaultListableBeanFactory进行关联并加载beandefinitions。
2.2 如何加载BeanDefinition
既然已经知道了整体的流程,那么配置文件是如何转换成IoC容器中的BeanDefinition的呢?
顺着上面的最后一步,跟进来看到loadBeanDefinitions的具体实现:
从源代码可以发现loadBeanDefinitions实际上调用了doLoadBeanDefinitions方法:
继续往下看:
从上面可以看到将Resource的内容解析成BeanDefinition主要分两步,第一步是先把Resource转换成待会可被解析的Document,第二步是将XML文件中的bean信息进行获取并将其构建到BeanDefinitionHolder中并且生成BeanDefinition。
3、注册阶段
在2中已经生成了BeanDefinition,那么是它如何放置在容器中,就是IoC的注册阶段了。
- 生成之后是如何放入IoC容器中,也就是上文生成的DefaultListableBeanFactory中?
从上面可以看到,注册的最后一步的代码是:
在代码中使用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());其实这里的getRegistry()返回的就是XmlReaderContext的BeanDefinitionRegistry。
而在2中创建的DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,而这里的BeanDefinitionRegistry就是DefaultListableBeanFactory的BeanDefinitionRegistry,而具体实现代码是在DefaultListableBeanFactory类中,具体实现如下:
在这里可以确认这里的registry就是DefaultListableBeanFactory,而调用注册的方法registerBeanDefinition()就是DefaultListableBeanFactory自己实现的,可以在DefaultListableBeanFactory找到该方法:
可以看到,它实际上是将BeanDefinition放入一个beanDefinitionMap里面,将bean的name做key,value是BeanDefinition的方式进行存储。存储的Map实际上就是DefaultListableBeanFactory的一个成员变量,定义如下: