IOC 容器的初始化包括 BeanDefinition 的 Resource 定位、加载和注册这三个基本的过程。我们以ApplicationContext 为例讲解,ApplicationContext 系列容器也许是我们最熟悉的,因为 Web 项目中使用的 XmlWebApplicationContext 就属于这个继承体系,还有 ClasspathXmlApplicationContext等,其继承体系如下图所示:
ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于 Bean 的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring应用提供了一个共享的 Bean 定义环境。
1、寻找入口
还有一个我们用的比较多的 ClassPathXmlApplicationContext,通过 main()方法启动:
ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
先看其构造函数的调用:
其实际调用的构造函数为:
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627222929797-1887432069.png)
还 有 像 AnnotationConfigApplicationContext 、 FileSystemXmlApplicationContext 、XmlWebApplicationContext 等都继承自父容器 AbstractApplicationContext主要用到了装饰器模式和策略模式,最终都是调用 refresh()方法。
2、获得配置路径
通 过 分 析 ClassPathXmlApplicationContext 的 源 代 码 可 以 知 道 , 在 创 建ClassPathXmlApplicationContext 容器时,构造方法做以下两项重要工作:
首先,调用父类容器的构造方法(super(parent)方法)为容器设置好 Bean 资源加载器。然后,再调用父类AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法设置 Bean 配置信息的定位路径。通 过 追 踪 ClassPathXmlApplicationContext 的 继 承 体 系 , 发 现 其 父 类 的 父 类AbstractApplicationContext 中初始化 IOC 容器所做的主要源码如下:
AbstractApplicationContext 的默认构造方法中有调用 PathMatchingResourcePatternResolver 的构造方法创建 Spring 资源加载器:
在设置容器的资源加载器之后,接下来 ClassPathXmlApplicationContext 执行 setConfigLocations()方法通过调用其父类AbstractRefreshableConfigApplicationContext的方法进行对Bean配置信息的定位,该方法的源码如下:
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627223346861-2122886104.png)
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627223410715-2020500024.png)
通过这两个方法的源码我们可以看出,我们既可以使用一个字符串来配置多个 Spring Bean 配置信息,也可以使用字符串数组,即下面两种方式都是可以的:
ClassPathResource res = new ClassPathResource("a.xml,b.xml");
多个资源文件路径之间可以是用” , ;
”等分隔。
ClassPathResource res =new ClassPathResource(new String[]{"a.xml","b.xml"});
至此,SpringIOC 容器在初始化时将配置的 Bean 配置信息定位为 Spring 封装的 Resource。
3、开始启动
SpringIOC 容器对 Bean 配置资源的载入是从 refresh()函数开始的,refresh()是一个模板方法,规定了IOC 容 器 的 启 动 流 程 , 有 些 逻 辑 要 交 给 其 子 类 去 实 现 。 它 对 Bean 配 置 资 源 进 行 载 入ClassPathXmlApplicationContext 通过调用其父类 AbstractApplicationContext 的 refresh()函数启动整个 IOC 容器对 Bean 定义的载入过程,现在我们来详细看看 refresh()中的逻辑处理:
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627223943359-1406566921.png)
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627224047051-803324894.png)
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627224119836-156739629.png)
refresh()方法主要为 IOC 容器 Bean 的生命周期管理提供条件,Spring IOC 容器载入 Bean 配置信息从 其 子 类 容 器 的 refreshBeanFactory() 方 法 启 动 , 所 以 整 个 refresh() 中“Configurable-ListableBean-Factory beanFactory = obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,我们前面说的载入就是从这句代码开始启动。refresh()方法的主要作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。它类似于对 IOC 容器的重启,在新建立好的容器中对容器进行初始化,对 Bean 配置资源进行载入。
4、创建容器
obtainFreshBeanFactory()方法调用子类容器的 refreshBeanFactory()方法,启动容器载入 Bean 配置信息的过程,代码如下:
AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法,容器真正调用的是其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory()方法,方法的源码如下:
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627224540913-130956300.png)
在这个方法中,先判断 BeanFactory 是否存在,如果存在则先销毁 beans 并关闭 beanFactory,接着创建 DefaultListableBeanFactory,并调用 loadBeanDefinitions(beanFactory)装载 bean 定义。
5、载入配置路径
AbstractRefreshableApplicationContext 中只定义了抽象的 loadBeanDefinitions 方法,容器真正调用的是其子类 AbstractXmlApplicationContext 对该方法的实现,AbstractXmlApplicationContext的主要源码如下:loadBeanDefinitions() 方 法 同 样 是 抽 象 方 法 , 是 由 其 子 类 实 现 的 , 也 即 在AbstractXmlApplicationContext 中。
![](https://img2020.cnblogs.com/blog/1585156/202006/1585156-20200627230054094-1823314535.png)
以 XmlBean 读取器的其中一种策略 XmlBeanDefinitionReader 为例。XmlBeanDefinitionReader 调用其父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions()方法读取Bean配置资源。由于我们使用 ClassPathXmlApplicationContext 作为例子分析,因此 getConfigResources 的返回值为 null,因此程序执行 reader.loadBeanDefinitions(configLocations)分支。