下图描述了IoC容器中的主要接口设计
这里写图片描述
简要分析:
这里写图片描述
我们从最基本的XmlBeanFactory看起,它是简单IoC容器系列的最底层实现,与我们在Spring的那些上下文相比,它只提供了最基本的IoC容器的功能. 我们可以认为直接的BeanFactory实现是IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器的高级表现形式.
继承自DefaultListableBeanFactory类,DefaultListableBeanFactory实际上已经包含了基本IoC容器所具有的重要功能.在Spring中实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的.而XmlBeanFactory继承了它之后,又增加了新的功能:它是一个可以读取以XML文件定义BeanDefinition的IoC容器。
在Spring 4中,该类已经不推荐被使用了
package org.springframework.beans.factory.xml;
@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
/**
* 通过给定的资源创建一个XmlBeanFactory实例。
*/
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
/**
* Create a new XmlBeanFactory with the given input stream,
* which must be parsable using DOM.
*/
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}
XmlBeanFactory又是怎样实现读取XML文件的呢?
如下是一个简单的编程式使用IoC容器的例子
// 创建IoC配置文件的抽象资源(包含BeanDefinition的定义信息)
ClassPathResource res = new ClassPathResource("bean.xml");
// 创建一个BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 创建一个载入BeanDefinition的读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
// 从定义好的资源位置读入配置信息
reader.loadBeanDefinitions(res);
上一节中我们了解了IoC容器建立的基本步骤,可以很方便的通过编程的方式来手动控制这些配置和容器的建立过程.但在Spring中为我们提供了许多已经定义好的容器实现,如ApplicationContext. 如图可以看到ApplicationContext在BeanFactory基础上添加的附加功能.
这里写图片描述
以常用的FileSytemXmlApplicationContext实现为例来说明ApplicationContext容器设计原理.
在其设计中,可以看到ApplicationContext的主要功能已经在它的基类AbstractXmlApplicationContext中实现了.在FileSystemXmlApplicationContext中,只需要实现和它自身设计相关的两个功能:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
这个refresh()过程会牵涉IoC容器启动的一系列复杂操作,对于不同的容器实现,这些操作都是类似的,所以在基类中将它们封装好.所以我们在FileSystemXml的设计中看到的只是一个简单的调用.
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
调用这个方法,可得到FileSystemResource的资源定位
可以发现,在FileSystemXmlApplicationContext中不管调用哪个构造函数,最终都会调用这个包含 refresh() 方法的构造函数,因此可得出结论: 触发对BeanDefinition资源定位过程的refresh()方法的调用是在FileSystemXmlApplicationContext的构造函数中启动的