在上一篇Spring技术内幕 :IoC容器的实现(一)—— Spring IoC的概述中,我们简单回顾了一下Spring IoC容器中依赖反转的概念以及Spring IoC的应用场景,本文将开始简单了解一下具体容器的继承关系及应用场景。
在Spring IoC容器的设计中,可以看到主要的两个容器系列,一个是实现BeanFactory接口的简单接口系列,这个系列的容器只实现了最基本的功能;另一个是ApplicationContext应用上下文,作为容器的高级形态存在。应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境作了许多适配。有了这两种基本的容器系列,基本上就可以满足用户对IoC容器使用的大部分需求了。
IoC容器为开发者管理对象之间的依赖关系提供了很多便利和基础服务。对于IoC容器的使用者来说,、经常接触到BeanFactory和ApplicationContext都可以看成是容器的具体表现形式。同时,在Spring中有各式各样的IoC容器的实现供用户选择和使用,使用什么样的容器完全取决于用户的需求。下图展示了这个容器系列的概况。
从上图可以看出,BeanFactory作为一个最基本的接口类出现在容器体系中。由此可以看出,作为IoC容器,接口类BeanFactory为提供给用户使用的IoC容器所设定了基本的功能规范。对于具体的IoC容器,需要满足BeanFactory这个基本的接口定义,所以在可以看到BeanFactory接口在继承体系中的作为最基本的接口类出现在Spring的IoC容器体系中。
在Spring提供的基本的IoC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系。我们知道,IoC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转的核心数据结构,依赖反转功能都是围绕BeanDefinition的处理完成的。
本节我们将简单了解IoC容器是如何设计的,首先通过下图来了解一下IoC容器的接口设计图。
下面对接口关系做一些简要的分析
在图中主要涉及了接口关系,任何具体的IoC容器都是在这个接口体系下实现,再进行功能上的扩展。总体而言,ApplicationContext既继承了BeanFactory IoC的基本功能,有继承了MessageSource、ResourceLoader、ApplicationEvnetPublisher接口,实现了对简单的IoC容器功能扩展的目的,即BeanFactory为IoC容器的基本形式,而各种ApplicationContext的实现是IoC容器对的高级表现形式。
BeanFactory提供了最基本的IoC容器的功能。
用户可以使用转义符“&”来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象和FactoryBean本身,即使用“&”得到的是FactoryBean本身。
注意:需要区分一下BeanFactory和FactoryBean的区别。BeanFactory从字面角度就可以知道它是一个Factory,是IoC的对象工厂,管理所有的Bean。FactoryBean则是一个Bean,是一个能产生或修饰对象生成的工厂类,它的实现与设计模式中的工厂模式和修饰器模式类似。 |
BeanFactory中提供了IoC的基本功能,有了它们,用户可以执行以下操作:
以下为BeanFactory的源码清单。
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
T getBean(String var1, Class var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
T getBean(Class var1) throws BeansException;
T getBean(Class var1, Object... var2) throws BeansException;
boolean containsBean(String var1);
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class> var2) throws NoSuchBeanDefinitionException;
Class> getType(String var1) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
虽然XmlBeanFactory在新版本Spring中已经不建议使用,但他相对简单,我们还是以XmlBeanFactory的实现来说明简单IoC容器的设计原理。下图为XmlBeanFactory的设计类继承关系。
以下为XmlBeanFactory源码清单。
@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, (BeanFactory)null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
}
可以看到,XmlBeanFactory继承了DefaultListableBeanFactory,需要明确,DefaultListableBeanFactory这个类非常重要,它实际上包含了基本IoC所具有的重要功能。并且在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。
从源码中可以看到,XmlBeanFactory在继承了DefaultListableBeanFactory后有添加了关于XML的新的功能。它是一个可以读取XML文件方式定义的BeanDefinition的IoC容器。
在构造XmlBeanFactory时,需要指定BeanDefinition的信息来源,封装为Resource(Spring中由来封装I/O操作的类)对象。将Resource作为构造参数传递给XmlBeanFactory,IoC容器就可以方便地定位到需要的BeanDefinition信息来对Bean完成容器的初始化和依赖注入过程。对XmlBeanDefinitionReader对象的初始化,以及使用这个对象对loadBeanDefinitions的调用载入BeanDefinitions的过程。
首先通过下图了解一下ApplicationContext提供的BeanFactory不具备的新特性
我们以常用的FileSystemXmlApplicationContext的实现为例来说明ApplicationContext容器的设计原理。FileSystemXmlApplicationContext源码清单如下所示。
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, (ApplicationContext)null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, (ApplicationContext)null);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
}
}
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
可以看到,FileSystemXmlApplicationContext继承了AbstractXmlApplicationContext ,其中AbstractXmlApplicationContext 实现了ApplicationContext应用上下文的主要功能。在FileSystemXmlApplicationContext中它主要实现了与自身设计的两个功能。
第一个为,如果应用直接使用FileSystemXmlApplicationContext,对于实例化这个应用的上下文的支持,同时启动refresh()过程,对应了FileSystemXmlApplicationContext中的三参构造方法。另外一个功能是与FileSystemXmlApplicationContext设计具体相关的功能,与怎样从文件系统中加载XML的Bean定义资源有关。对应了getResourceByPath(String path)方法。
本文从BeanFactory和ApplicationContext两个系列简单介绍了IoC容器的设计原理,并通过部分源码体会了具体的IoC容器是如何在以前的基础上进一步完善功能的。