IOC容器的实现

什么是 IOC

所谓 IOC,inversion of controller,也就是控制反转。控制?控制什么呢?控制着对象之间的依赖关系,也就是说Spring 反转了对象之间的依赖关系。

举个例子,对象A中需要对象B(也叫A依赖着B),A直接new出一个B,此时,依赖关系由A控制;而在IOC中,A不再亲自new出一个B,而是由IOC容器来生成B,并赋给A,此时,依赖关系由IOC 容器控制,也就是说,控制从A反转到IOC 容器

IOC 容器

IOC可以控制着依赖关系,那么IOC容器有多少种呢?

BeanFactory接口

IOC 容器有多个接口,我们可以组合实现不同的接口,得到不同的IOC容器。其中最基本的接口是BeanFactory,该接口提供了最基本的功能指示,实现了接口中的功能,就能得到一个最基本的IOC容器,接口里的功能有getBean/containsBean等。

实现BeanFactory接口的IOC容器只有最简单的功能,甚至连定位BeanDefinition都要额外调用其他的功能模块来完成,对编程人员来说很不方便,所以Spring还有其它的接口,其中,ApplictionContext接口继承了其它许多的接口,功能丰富了许多。

ApplictionContext接口

该接口扩展了许多额外的接口,多了许多功能,如支持不同的信息源(方便了国际化)、支持事件、访问定位资源等等。

IOC的装载物

IOC容器负责控制反正,也就是在一个对象A需要某个对象B时,IOC容器必须及时地给对象A注入一个对象B,所以IOC容器里装着的是对象B或对象C。
有的童鞋问了,容器是怎么保存对象B的呢?直接new出个对象保存起来?
回答:差不多,容器中保存了一种BeanDefiniton,后者代表着对象B或C,不过值得注意的是,IOC 容器里保存的只是对象的相关信息,此时对象还没有被new出来。

IOC的初始化

我们已经知道了IOC容器的种类(甚至可以自定义新的容器),知道 IOC 容器中保存的是BeanDefinition,那么IOC容器是怎么运作的呢?

IOC容器想要运行起来,首先必须得初始化并启动起来,启动包括了三个过程:BeanDefiniton定位,BeanDefinition 解析,BeanDefiniton 注册。

BeanDefinition定位

一般地,我们在使用 IOC 时,必须要指明哪个对象依赖着哪个额外的对象,通常我们是在 xml 文件里定义这些依赖关系。IOC容器启动时,必须首先寻找到这些xml文件,也就是所谓的 BeanDefinition 定位。

BeanDefinition解析

找到了 XML 文件,下一步自然是解析出里面的信息,不然计算机只是拿到了一个XML文件,不知道对象之间的依赖关系呀。对 JVM 中类的加载过程熟悉的童鞋,对解析这一个过程自然也熟悉,无非就是解析 XML 文件,从中提取重要的信息。解析完后,BeanDefinition从XML文件流转到了内存中,成为一种IOC容器能识别是数据结构。

BeanDefinition注册

IOC容器获晓了BeanDefinition,可它也没法管理BeanDefinition呀。为了管理它们,IOC 容器将它们全都放入一个HashMap中,需要时直接从哈希表里获取。

依赖注入

IOC 启动起来后,知道了程序员们所定义的依赖关系,比如A依赖B,已经算是不错了。但是,知道是知道,IOC此时还没有将B注入A,没体现出控制反转的能力。

A第一次向IOC索要B时,容器开始一种被称为依赖注入的过程,生成一个对象B,并注入到A中去。

有童鞋问了,是怎么生成对象B的咧?直接new ?
回答:非也非也,IOC并不直接new对象,而是调用BeanUtils工具类的方法,以JVM反射的方式生成一个对象,或者调用GGLIB 来实例化Bean对象。

又有童鞋问了,是怎么注入到A中的?A是怎么知道B在哪里的?
回答:关键点是A的BeanDefinition里的属性,我们将该属性的值设为B就可以了,IOC 在运行起来后,A会根据自己的BeanDefinition来找到B。

结语

到这里为止,A不必自己new出一个B,借着IOC的帮助就能得到了一个对象B,IOC容器的功能就真正的体现出来了。