天天看点

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

在Spring IoC容器的设计中,两个主要容器系列

  • 实现BeanFactory接口的简单容器系列

    这系列容器只实现了容器的最基本功能

  • ApplicationContext应用上下文

    作为容器的高级形态而存在。在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境作了许多适配。

1 Spring的IoC容器系列

对IoC容器的使用者来说,我们经常接触到的BeanFactory和ApplicationContext都可以看成是容器的具体表现形式。

如果深入到Spring的实现中去看,所说的IoC容器,实际上代表着一系列功能各异的容器产品,只是容器的功能有大有小,有各自的特点。

Spring有各式各样的IoC容器的实现供用户选择和使用。

使用什么样的容器完全取决于用户的需要,但在使用之前如果能够了解容器的基本情况,那对容器的使用是非常有帮助的,就像我们在购买商品前对商品进行考察和挑选那样

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

Spring的IoC容器系列概况

作为IoC容器,需要为它的具体实现指定基本的功能规范,这个功能规范的设计表现为接口类

BeanFactory

在这些Spring提供的基本IoC容器的接口定义和实现的基础上,Spring通过定义

BeanDefinition

来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。

BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。

IoC容器是用来管理对象依赖关系的,

BeanDefinition

就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。

2 Spring IoC容器的设计

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

IoC容器的接口设计图

  • 从接口BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,是一条主要的BeanFactory设计路径

    BeanFactory

    定义了基本的IoC容器的规范。包括了getBean()(通过这个方法可以从容器中取得Bean)。

    HierarchicalBeanFactory

    接口在继承了

    BeanFactory

    后,增加了

    getParentBeanFactory()

    ,使

    BeanFactory

    具备了双亲IoC容器的管理功能。

    在接下来的

    ConfigurableBeanFactory

    中,定义了一些对

    BeanFactory

    的配置功能,比如通过

    setParentBeanFactory()

    设置双亲IoC容器,通过

    addBeanPostProcessor()

    配置Bean后置处理器,等等
  • 第二条接口设计主线是,以

    ApplicationContext

    为核心的接口设计

    我们常用的应用上下文基本上都是ConfigurableApplicationContext或者WebApplicationContext的实现

    在这个接口体系中,ListableBeanFactory和HierarchicalBeanFactory两个接口,连接BeanFactory接口定义和ApplicationConext应用上下文的接口定义。

    在ListableBeanFactory接口中,细化了许多BeanFactory的接口功能,比如定义了getBeanDefinitionNames()接口方法;对于HierarchicalBeanFactory接口,我们在前文中已经提到过;对于ApplicationContext接口,它通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单IoC容器的基础上添加了许多对高级容器的特性的支持

  • 这里涉及的是主要接口关系,而具体的IoC容器都是在这个接口体系下实现的,比如

    DefaultListableBeanFactory

    ,这个基本IoC容器的实现就是实现了

    ConfigurableBeanFactory

    ,从而成为一个简单IoC容器的实现。

    像其他IoC容器,比如

    XmlBeanFactory

    ,都是在

    DefaultListableBeanFactory

    的基础上做扩展
  • 这个接口系统是以

    BeanFactory

    ApplicationContext

    为核心

    而BeanFactory又是IoC容器的最基本接口,在ApplicationContext的设计中,一方面,可以看到它继承了BeanFactory接口体系的接口,具备了BeanFactory IoC容器的基本功能

    另一方面,通过继承MessageSource、ResourceLoadr、ApplicationEventPublisher这些接口,BeanFactory为ApplicationContext赋予了更高级的IoC容器特性。

    对于ApplicationContext而言,为了在Web环境中使用它,还设计了WebApplicationContext接口,而这个接口通过继承ThemeSource接口来扩充功能。

3 BeanFactory的应用场景

BeanFactory 是最原始的 ioc 容器,有以下方法 1.getBean2.判断是否有 Bean,containsBean3.判断是否单例 isSingleton。

BeanFactory接口定义了IoC容器最基本的形式,不关心 Bean 是怎样定义和加载的。如果我们想要知道一个工厂具体产生对象的过程,则要看这个接口的实现类。

在Spring的代码实现中,

BeanFactory

只是一个接口,并没有给出容器的具体实现,

DefaultListableBeanFactory

XmlBeanFactory

ApplicationContext

等都可以看成是容器附加了某种功能的具体实现

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

可以使用转义符“&”得到

FactoryBean

本身,用来区分通过容器来获取

FactoryBean

产生的对象和获取

FactoryBean

本身

举例来说,如果

myJndiObject

是一个

FactoryBean

,那么使用

&myJndiObject

得到的是

FactoryBean

,而不是

myJndiObject

这个

FactoryBean

产生出来的实例

  • 注意

    理解上面这段话需要很好地区分

    FactoryBean

    BeanFactory

    一个是Factory,也就是IoC容器或对象工厂,一个是Bean

    所有的Bean都是由

    BeanFactory

    (也就是IoC容器)管理

    FactoryBean

    ,这个Bean不是简单的Bean,而是一个能产生或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

BeanFactory

接口设计了

getBean

,可以取得IoC容器中管理的Bean,Bean的取得是通过指定名字来索引的。

如果需要在获取Bean时对Bean的类型进行检查,

BeanFactory

接口定义了带有参数的

getBean

,增加了对Bean检索的类型的要求。

用户可以通过

BeanFactory

接口方法中的

getBean

来使用Bean名字,从而在获取Bean时,如果需要获取的Bean是prototype类型的,用户还可以为这个prototype类型的Bean生成指定构造函数的对应参数。这使得在一定程度上可以控制生成prototype类型的Bean。

有了

BeanFactory

的定义,用户可以执行以下操作

  • containsBean

    让用户能够判断容器是否含有指定名字的Bean

  • isSingleton

    来查询指定名字的Bean是否是Singleton类型

    对于Singleton属性,用户可以在BeanDefinition中指定。

  • isPrototype

    查询指定名字的Bean是否是prototype类型的

    与Singleton属性一样,这个属性也可以由用户在BeanDefinition中指定。

  • isTypeMatch

    查询指定名字的Bean的Class类型是否是特定的Class类型

    这个Class类型可以由用户来指定。

  • getType

    查询指定名字的Bean的Class类型。

  • getAliases

    查询指定了名字的Bean的所有别名,这些别名都是用户在BeanDefinition中定义的

    IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

4 BeanFactory容器的设计原理

BeanFactory

提供了使用IoC容器的规范

在这个基础上,Spring还提供了符合这个IoC容器接口的一系列容器的实现供开发人员使用

XmlBeanFactory

的实现为例来说明简单IoC容器的设计原理

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

XmlBeanFactory

可以看到,作为一个简单IoC容器系列最底层实现的

XmlBeanFactory

与我们在Spring应用中用到的那些上下文相比,有一个非常明显的特点:它只提供最基本的IoC容器的功能。理解这一点有助于我们理解

ApplicationContext

与基本的

BeanFactory

之间的区别

我们可以认为直接的

BeanFactory

实现是IoC容器的基本形式,而各种

ApplicationContext

的实现是IoC容器的高级表现形式

下面就从我们比较熟悉的XmlBeanFactory的实现入手进行分析,来看看一个基本的IoC容器是怎样实现的。

仔细阅读

XmlBeanFactory

的源码,在一开始的注释里会看到功能的简要说明,从代码的注释还可以看到,这是Rod Johnson在2001年就写下的代码,可见这个类应该是Spring的元老类了。继承自

DefaultListableBeanFactory

,后者非常重要,是我们经常要用到的一个IoC容器的实现,比如在设计应用上下文

ApplicationContext

时就会用到它

DefaultListableBeanFactory

实际上包含了基本IoC容器所具有的重要功能

在Spring中,实际上是把

DefaultListableBeanFactory

作为一个默认的功能完整的IoC容器来使用的。

XmlBeanFactory

在继承了

DefaultListableBeanFactory

容器的功能的同时,增加了新的功能,是一个可以读取以XML文件方式定义的BeanDefinition的IoC容器

  • 这些实现XML读取的功能是怎样实现的呢?

    在XmlBeanFactory中,初始化了一个

    XmlBeanDefinitionReader

    ,那些以XML方式定义的

    BeanDefinition

    就有了处理的地方

构造 XmlBeanFactory这个IoC容器时,需要指定

BeanDefinition

的来源,而这个信息来源需要封装成Spring中的

Resource

类来给出。Resource是Spring用来封装I/O操作的类。比如,我们的

BeanDefinition

信息是以XML文件形式存在的,那么可以使用像

ClassPath-Resource res = new ClassPathResource("beans.xml");

这样具体的ClassPathResource来构造需要的Resource,然后将Resource作为构造参数传递给XmlBeanFactory构造函数。

这样,IoC容器就可以方便地定位到需要的

BeanDefinition

信息来对Bean完成容器的初始化和依赖注入过程。

XmlBeanFactory

的功能是建立在

DefaultListableBeanFactory

这个基本容器的基础上的,并在这个基本容器的基础上实现了其他诸如XML读取的附加功能。

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

在构造方法中需要得到

Resource

对XmlBeanDefinitionReader初始化,以及使用这个对象来完成对loadBeanDefinitions的调用,就是这个调用启动从Resource中载入BeanDefinitions的过程

我们看到XmlBeanFactory使用了DefaultListableBeanFactory作为基类,DefaultListable-BeanFactory是很重要的一个IoC实现,在其他IoC容器中,比如ApplicationContext,其实现的基本原理和XmlBeanFactory一样,也是通过持有或者扩展DefaultListableBeanFactory来获得基本的IoC容器的功能的。

参考XmlBeanFactory的实现,我们以编程的方式使用DefaultListableBeanFactory。从中我们可以看到IoC容器使用的一些基本过程

编程式使用IoC容器

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

这样,我们就可以通过factory对象来使用 DefaultListableBeanFactory这个IoC容器了。在使用IoC容器时,需要如下几个步骤:

  • 创建IoC配置文件的抽象资源,这个抽象资源包含了

    BeanDefinition

    的定义信息。
  • 创建一个BeanFactory,这里使用

    DefaultListableBeanFactory

  • 创建一个载入

    BeanDefinition

    的读取器,这里使用

    XmlBeanDefinitionReader

  • 从定义好的资源位置读入配置信息,具体的解析过程由

    XmlBeanDefinitionReader

    来完成。完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。

5 ApplicationContext的应用场景

相比那些简单拓展

BeanFactory

的基本IoC容器,开发人员常用的

ApplicationContext

除了能够提供前面介绍的容器的基本功能外,还为用户提供了以下的附加服务,可以让客户更方便地使用。所以说,ApplicationContext是一个高级形态意义的IoC容器,

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理
  • 支持不同的信息源

    扩展了

    MessageSource

    ,可以支持国际化的实现,为开发多语言版本的应用提供服务。
  • 访问资源

    对ResourceLoader和Resource的支持上,可以从不同地方得到Bean定义资源。这种抽象使用户程序可以灵活地定义Bean定义信息,尤其是从不同的I/O途径得到Bean定义信息。这在接口关系上看不出来,不过一般来说,具体

    ApplicationContext

    都是继承了

    DefaultResourceLoader

    的子类。因为

    DefaultResourceLoader

    AbstractApplicationContext

    的基类
  • 支持应用事件

    实现了

    ApplicationEventPublisher

    ,从而在上下文中引入了事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
  • 在ApplicationContext中提供的附加服务

    这些服务使得基本IoC容器的功能更丰富,使得ApplicationContext与简单的BeanFactory相比,对它的使用是一种面向框架的使用风格,所以一般建议在开发应用时使用ApplicationContext作为IoC容器的基本形式。

6 ApplicationContext容器的设计原理

以常用的

FileSystemXmlApplicationContex

t的实现为例

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

FileSystemXmlApplicationContext

FileSystemXmlApplicationContext

的设计中,我们看到

ApplicationContext

应用上下文的主要功能已经在

FileSystemXmlApplicationContext

AbstractXmlApplicationContext

中实现了,在

FileSystemXmlApplicationContext

中,作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。

一个功能是,如果应用直接使用

FileSystemXmlApplicationContext

,对于实例化这个应用上下文的支持,同时启动IoC容器的refresh()过程。

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

这个refresh()过程会牵涉IoC容器启动的一系列复杂操作,同时,对于不同的容器实现,这些操作都是类似的,因此在基类中将它们封装好。所以,我们在FileSystemXml的设计中看到的只是一个简单的调用。

另一个功能是与FileSystemXmlApplicationContext设计具体相关的功能,这部分与怎样从文件系统中加载XML的Bean定义资源有关。

通过这个过程,可以为在文件系统中读取以XML形式存在的BeanDefinition做准备,因为不同的应用上下文实现对应着不同的读取BeanDefinition的方式,在FileSystemXml-ApplicationContext中的实现代码如下:

IoC容器系列的设计与实现(基于Spring5.0.4)1 Spring的IoC容器系列2 Spring IoC容器的设计3 BeanFactory的应用场景4 BeanFactory容器的设计原理5 ApplicationContext的应用场景6 ApplicationContext容器的设计原理

可以看到,调用这个方法,可以得到FileSystemResource的资源定位。