您的位置:首页 > 移动开发

Spring IoC(一)IoC容器的设计与实现:BeanFactory与ApplicationContext

2018-02-07 10:21 453 查看
在写BeanFactory与ApplicationContext 之前,我想先简单聊一聊Spring IoC 容器,希望能给大家一个参考。如果你对这反面的知识比较了解,可以直接跳过。

(一)Spring IoC 容器概述

1.1IOC & DI

控制反转(Inversion of Control):其思想是反转资源获取的方向。 传统的资源查找方式要求组件向容器发起请求查找资源。 作为回应, 容器适时的返回资源。

而应用了 IOC 之后, 则是容器主动地将资源推送给它所管理的组件, 组件所要做的仅是选择一种合适的方式来接受资源。 这种行为也被称为查找的被动形式。

如果要确定是哪些方面被反转了,结论是:依赖对象的获得被反转了。

它包括依赖注入(Dependency Injection)和依赖查找(Dependency Lookup)。

依赖注入(Dependency Injection) : IOC 的另一种表述方式。即组件以一些预先定义好的方式接受来自如容器的资源注入。 相对于 IOC 而言,这种表述更直接。

1.2IoC 容器的优点

在Spring 中,IoC容器是实现控制反转这个理念的载体,它可以在对象生成或初始化时直接将数据注入到对象中。这种依赖注入可以是递归进行的,对象被逐层注入。IoC 容器把对象的依赖关系有序的建立起来,简化了对象依赖关系的管理,在很大程度上简化了面向对象系统的复杂性。

通过使用IoC 容器,对象依赖关系的管理被反转了,被反转到IoC 容器中,对象之间的相互依赖关系由IoC 容器管理,并由IoC 容器完成对象的注入。这在很大程度上简化了应用的开发,把对象从复杂的对象依赖关系解放出来。

IoC 容器可以把资源的获取方向反转,让IoC 容器主动管理这些依赖关系,将这些关系注入到组件中,可以使依赖关系的适配和管理变得更加灵活。

如果合作对象的引用或依赖关系的管理由具体对象来完成,会导致代码的高度耦合和可测试性降低,这对复杂的面向对象系统的设计是非常不利的。有了IoC 容器之后,这些依赖关系可以通过把对象的依赖注入交给框架或者IoC 容器来完成,这种从具体对象手中交出控制的做法是非常有价值的,它可以在解耦代码的同时提高代码的可测试性。

(二)BeanFactory与 ApplicationContext

2.1IoC 容器系列

在Spring IoC 容器的设计中,有两个主要的容器系列,一个是实现BeanFactory 接口的简单容器系列,这系列容器只实现的容器的基本功能;另一个是ApplicationContext 应用上下文,它作为容器的高级系列而存在。应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境做了许多适配。有了这两种基本的容器系列,基本上就可以满足用户对IoC 容器使用的大部分需求了。

我们可以在代码的角度来查看一下这些容器的设计情况。



通过继承结构我们可以发现BeanFactory 是作为一个最基本的接口类存在于Spring IoC 容器体系中的。这个继承结构图可以作为参考,下面主要介绍BeanFactory与 ApplicationContext。

2.2BeanFactory

2.2.1BeanFactory 应用场景

BeanFactory 提供了最基本的IoC 容器的实现,关于这些功能的定义,如下:



BeanFactory 接口定义了IoC 容器的最基本形式,并且提供了IoC 容器所应该遵守的最基本的服务契约。同时,这也是IoC 容器所应该遵守的最底层和最基本的编程规范,勾勒出了最基本的IoC 容器轮廓。很显然BeanFactory 只是一个接口,所以并没有给出方法的实现。其中DefaultListableBeanFactory、XmlBeanFactory 和ApplicationContext都可以看作是容器体系中的具体容器产品。在Spring 中,所有的Bean 都是由BeanFactory 来进行管理的。

BeanFactory 中设计了getBean() 方法,这个方法是IoC 容器API 的主要方法,通过这个方法,可以获取到由IoC 容器所管理的Bean。通过上面的图我们可以知道,BeanFactory 支持多种方式获取Bean。下面来具体了解一下其中一些方法的作用。

containsBean() :可以让用户判断容器中是否含有指定名字的Bean。

isSingleton() :查询指定名字的Bean 是不是单例的。默认情况下,Spring IoC 容器中的Bean 是单例的。用户可以在BeanDefinition 中指定。

isPrototype():查询指定名字的Bean 是不是原型的。与isSingleton() 方法类似。

isTypeMatch():用来查询指定名字的Bean 的Class 类型是否是特定的Class 类型。

getType():获取指定名字的Bean 的Class 类型。

getAliases():获取指定名字的Bean 的所有别名。这些别名可以在BeanDefinition 定义。

这些定义的接口方法规划了IoC 容器的基本特征。BeanFactory 允许使用不同的方式来检索Bean,从而将以前用户自己创建与管理Bean 的方式中解放出来。这些检索方法是Spring IoC 容器的最基本的入口。

2.2.2BeanFactory 容器的设计原理

BeanFactory 接口提供了使用IoC 容器的规范。在这基础上,Spring 还提供了一些符合这个容器接口的具体实现。这里以XMLBeanFactory 为例(现在这个类已经被废弃了)来简单说明IoC 容器的设计原理。下面是这个类的部分继承结构。



XMLBeanFactory 作为IoC 容器最底层的实现,与ApplicationContext 相比有明显的特点:它只提供最基本的IoC 容器的功能。我们可以把BeanFactory 实现时IoC 容器的基本形式,而各种ApplicationContext 的实现是IoC 容器的高级变现形式。接下来就从XMLBeanFactory 入手来简要分析IoC 容器是如何实现的。下面是去掉注释后的XMLBeanFactory 底层源码。

public class XmlBeanFactory extends DefaultListableBeanFactory {

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}


XMLBeanFactory 继承自DefaultListableBe
4000
anFactory ,后者这个类非常重要,是经常可以用到的一个IoC 容器的实现。DefaultListableBeanFactory 这个包含了基本IoC 容器所具有的的重要功能,也是很多IoC 容器系列都会用到的一个基本IoC 产品。比如在ApplicationContext 中就会用到它。实现涉及到了1000多行代码,有很多的方法,这里不方便贴出,有兴趣的话大家可以自己查看。

在Spring 中实际上把DefaultListableBeanFactory 作为一个默认的功能完整的IoC 容器来使用。XMLBeanFactory 在继承了DefaultListableBeanFactory 的同时,扩展了其中的方法。由它的名字我们可以看出,XMLBeanFactory 是一个与XML 文件相关的BeanFactory。

这个类是如何读取XML 的呢?其实,对于XML 文件的信息的读取并不是由XMLBeanFactory 直接完成的。在上面的源码中,可以看出在XMLBeanFactory 中定义并初始化了一个XmlBeanDefinitionReader 对象,有了这个reader 对象,那些以XML 方式定义的BeanDefinition 就有了处理的地方。实际上,对于XML 形式的信息处理是由XmlBeanDefinitionReader 来完成的。

在XMLBeanFactory 构造函数中,需要指定BeanDefinition 的信息来源。Resource 是Spring 用来封装I/O 操作的接口,由其具体的实现类来加载XML 文件,从而来完成Bean 的初始化与依赖注入过程。下面是Resource 相关的继承结构图。



XMLBeanFactory 的功能是建立在DefaultListableBeanFactory 类基础上的,并在这个基本容器的基础上实现了一些关于XML 文件的操作。XMLBeanFactory 中的源码很好理解:在XMLBeanFactory 的构造函数中得到需要的Resource 对象对XmlBeanDefinitionReader 对象初始化,以及使用这个对象来完成loadBeanDefinitions() 方法的调用,loadBeanDefinitions() 同时也是IoC 容器初始化的重要组成部分。

通过对过程的分析,我们就可以总结一下IoC 容器的使用过程(这里通过代码的方式总结),这个过程可以参考上面的XmlBeanFactory 源码实现。

ClassPathResource res = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);


过程分析:

创建IoC 配置文件的抽象信息,这个抽象资源包含了BeadDefinition 的定义信息。

创建一个BeanFactory,这里以DefaultListableBeanFactory 为例。

创建一个BeadDefinition 的读取器,通过一个回调配置给BeanFactory。

从定义好的资源位置读取配置信息,具体的解析过程由XmlBeanDefinitionReader 对象来完成。在完成载入和注册Bean之后,需要的IoC 容器就建立起来了。这时候就可以直接使用了。

2.3ApplicationContext

2.3.1ApplicationContext 应用场景

在Spring 中,系统已经为用户提供了定义好的容器的实现,从而简化我们的开发。相比那些简单扩展BeanFactory 的基本IoC 容器,我们常用的是AppliCationContext,它除了提供了前面介绍的基本IoC 容器功能外,还提供了一些附加功能,因此让用户更方便的使用。下面是ApplicationContext 接口关系图。



有些接口为ApplicationContext 提供了一些BeanFactory 所不具备的新特性。

支持不同的信息源。ApplicationContext 扩展了MessageResource 接口,这些信息源的功能可以支持国际化的实现。

访问资源。这一特性体验在Resource 与ResourceLoader 上,这样一来,我们可以从不同的地方得到Bean 定义资源。

支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean 的生命周期的结合为Bean 的管理提供了便利。

在ApplicationContext 中提供的附加服务。这些服务使得基本的IoC 容器的基本功能更加丰富。

2.3.2ApplicationContext 容器的设计原理

在ApplicationContext 容器中,这里以FileSystemXmlApplicationContext 的实现原理来说明ApplicationContext 容器的设计原理。下面是FileSystemXmlApplicationContext 的继承结构。



FileSystemXmlApplicationContext 继承自AbstractXmlApplicationContext,AbstractXmlApplicationContext 中实现了FileSystemXmlApplicationContext 的大部分功能。在FileSystemXmlApplicationContext 中,作为一个具体的应用上下文,只需要实现和它自身相关的两个功能。

一个功能是,在实例化FileSystemXmlApplicationContext 时,启动IoC 容器的refresh() 过程。主要相关代码如下:

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {

super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}


这个refresh() 过程会涉及到IoC 容器启动时的一系列复杂操作,同时对于不同的容器的实现,这些操作都是类似的,因此在基类中将它们封装好。所以这里只简单分析一下其调用过程。关于refresh() 在IoC 容器中的具体实现,会在后面的博文中讲述。

另一个功能是FileSystemXmlApplicationContext 设计的相关功能,这个功能决定怎样从文件系统中加载XML 的Bean 资源。下面是相关源码:

@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}


通过这个过程,可以为文件系统中以XML 文件形式存在的BneaDefinition 做准备。从而通过这个方法,得到FileSystemXmlApplicationContext 的资源定位。

参考资料

《Spring 技术内幕》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring IoC 设计原理一