IoC容器系列的设计与实现(基于Spring5.0.4)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 在Spring IoC容器的设计中,两个主要容器系列实现BeanFactory接口的简单容器系列这系列容器只实现了容器的最基本功能ApplicationContext应用上下文作为容器的高级形态而存在。

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

  • 实现BeanFactory接口的简单容器系列
    这系列容器只实现了容器的最基本功能
  • ApplicationContext应用上下文
    作为容器的高级形态而存在。在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境作了许多适配。

1 Spring的IoC容器系列

对IoC容器的使用者来说,我们经常接触到的BeanFactory和ApplicationContext都可以看成是容器的具体表现形式。
如果深入到Spring的实现中去看,所说的IoC容器,实际上代表着一系列功能各异的容器产品,只是容器的功能有大有小,有各自的特点。

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

img_002e4e76908acba0e4a2491d6c3fca81.png
Spring的IoC容器系列概况

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

在这些Spring提供的基本IoC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。
BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。
IoC容器是用来管理对象依赖关系的,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。

2 Spring IoC容器的设计

img_22dab076cf1c90cbc808ec5a83777550.png
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的基础上做扩展
  • 这个接口系统是以BeanFactoryApplicationContext为核心
    而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只是一个接口,并没有给出容器的具体实现,DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等都可以看成是容器附加了某种功能的具体实现

img_e24d5accde2d392f25a5300b9dfe80b8.png

可以使用转义符“&”得到 FactoryBean本身,用来区分通过容器来获取 FactoryBean产生的对象和获取 FactoryBean本身
举例来说,如果 myJndiObject是一个 FactoryBean,那么使用 &myJndiObject得到的是 FactoryBean,而不是 myJndiObject这个 FactoryBean产生出来的实例

  • 注意
    理解上面这段话需要很好地区分FactoryBeanBeanFactory
    一个是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中定义的


    img_929ae0b1427f10a62c470db22b27658d.png

4 BeanFactory容器的设计原理

BeanFactory提供了使用IoC容器的规范
在这个基础上,Spring还提供了符合这个IoC容器接口的一系列容器的实现供开发人员使用
XmlBeanFactory的实现为例来说明简单IoC容器的设计原理

img_f895816ff6c6cb40e8915b27b6fd425c.png
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读取的附加功能。

img_c3e404542216fed18fbf3cb056b496eb.png

在构造方法中需要得到 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容器,

img_20741fd24325d5e8db6c6ae7c3373a56.png

  • 支持不同的信息源
    扩展了MessageSource,可以支持国际化的实现,为开发多语言版本的应用提供服务。
  • 访问资源
    对ResourceLoader和Resource的支持上,可以从不同地方得到Bean定义资源。这种抽象使用户程序可以灵活地定义Bean定义信息,尤其是从不同的I/O途径得到Bean定义信息。这在接口关系上看不出来,不过一般来说,具体ApplicationContext都是继承了DefaultResourceLoader的子类。因为DefaultResourceLoaderAbstractApplicationContext的基类
  • 支持应用事件
    实现了ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。
  • 在ApplicationContext中提供的附加服务
    这些服务使得基本IoC容器的功能更丰富,使得ApplicationContext与简单的BeanFactory相比,对它的使用是一种面向框架的使用风格,所以一般建议在开发应用时使用ApplicationContext作为IoC容器的基本形式。

6 ApplicationContext容器的设计原理

以常用的FileSystemXmlApplicationContext的实现为例

img_cfcdb56dcbb116b02f71fa27aea964bd.png
FileSystemXmlApplicationContext

FileSystemXmlApplicationContext的设计中,我们看到ApplicationContext应用上下文的主要功能已经在FileSystemXmlApplicationContext的基类AbstractXmlApplicationContext中实现了,在FileSystemXmlApplicationContext中,作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。
一个功能是,如果应用直接使用FileSystemXmlApplicationContext,对于实例化这个应用上下文的支持,同时启动IoC容器的refresh()过程。

img_f8fad6807ca903c201fccd8ceab22676.png

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

另一个功能是与FileSystemXmlApplicationContext设计具体相关的功能,这部分与怎样从文件系统中加载XML的Bean定义资源有关。
通过这个过程,可以为在文件系统中读取以XML形式存在的BeanDefinition做准备,因为不同的应用上下文实现对应着不同的读取BeanDefinition的方式,在FileSystemXml-ApplicationContext中的实现代码如下:

img_4902148a73e64dc2ad808ad3b6e0e5e2.png

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

目录
相关文章
|
1月前
|
Java 测试技术 开发工具
ApplicationArguments读取应用程序参数并注入到IOC容器
ApplicationArguments读取应用程序参数并注入到IOC容器
ApplicationArguments读取应用程序参数并注入到IOC容器
|
2月前
|
XML Java 数据格式
Spring IoC容器初始化过程(xml形式)
Spring IoC容器初始化过程(xml形式)
46 0
|
2月前
|
Java Spring
Spring5深入浅出篇:Spring中ioc(控制反转)与DI(依赖注入)
Spring5深入浅出篇:Spring中ioc(控制反转)与DI(依赖注入)
|
1月前
|
Java 数据库连接 API
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
49 0
|
20天前
|
XML Java 数据格式
Spring(一)IOC小案例
Spring(一)IOC小案例
|
1月前
|
存储 前端开发 Java
springboot中的第二个IOC容器BootstrapContext
springboot中的第二个IOC容器BootstrapContext
springboot中的第二个IOC容器BootstrapContext
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (下)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
|
2月前
|
XML 缓存 Java
Spring IoC原理解读
Spring IoC原理解读
26 0
|
2月前
|
Java 测试技术 开发者
探究 Spring Boot 的核心:IOC 和 AOP
Spring Boot 作为一种简化 Spring 应用开发的工具,继承了 Spring 框架的核心概念,其中最重要的是控制反转(IOC)和面向切面编程(AOP)。它们是 Spring 框架的基础,同时也深深植根于 Spring Boot 中。本文将讨论 IOC 和 AOP 的概念以及它们在 Spring Boot 中的应用。
59 4