Spring IoC 容器的设计与实现原理

上一篇文章讲解的是IOC的原理,这一篇文章主要讲解Spring IoC 容器的设计与实现原理

1.spring的IOC容器

在 Spring IoC 容器的设计中,容器有两个系列,可以看成是容器的具体表现形式:

  • BeanFactory 简单容器:实现了容器的基本功能,典型方法如 getBean、containsBean、isSingleton;

  • ApplicationContext 应用上下文:在简单容器的基础上,增加上下文的特性。

解读:

为什么要设计两个系列,而不是一个?这就涉及到架构设计的模式了,底层定义核心流程,上层扩展功能实现,高内聚、低耦合。在架构设计中,这样的分层是很有必要的,可以随时替换掉一个抽象层。

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

解读:

BeanDefinition 事实上就是 Bean 的定义在运行时的表现。无论是 xml 配置的 Bean,还是注解定义的 Bean,又或者是自定义扫描进来的 Bean,最终都通过 BeanDefinition 来承载。如果有自定义的 xml 标签,解析后也是生成 BeanDefinition 注册到 IOC 中。这样设计,IOC 只需要关心 BeanDefinition 即可,极大增加了扩展性和灵活性。当我们 getBean 的时候,如果 Bean 还没有初始化,容器就会找到 BeanDefinition,然后根据 BeanDefinition 初始化 Bean 及其依赖。

2.springIOC容器的设计

IoC容器的接口设计如图所示:

BeanFactory 定义了基本的 IoC 容器的规范,包括了 getBean 方法。HierarchicalBeanFactory 接口在继承了 BeanFactory 后,增加了getParentBeanFactory 方法,使 BeanFactory 具备了双亲IoC容器的管理功能。在接下来的 ConfigurableBeanFactory 中,定义了一些对 BeanFactory 的配置功能,比如通过 setParentBeanFactory 方法设置双亲IoC容器,通过 addBeanPostProcessor 方法配置Bean后置处理器。

解读:

可以看到 BeanFactory 只定义了基本功能,是一个最核心的容器接口定义。在 BeanFactory 的基础上 Spring 通过继承逐层扩充容器的能力。理解如此多的工厂接口就是在理解 Spring 的设计模式,正如前面所说,这里的继承关系也是架构分层的体现。通过继承和扩充,在 ConfigurableBeanFactory 中基本完成了 BeanFactory 系列的接口定义。当然了,接口分层后,BeanFactory 的每一层具体实现也是分层的,后面会具体解读。这里可以看到面向接口开发极大提高了扩展性和灵活性。

以 ApplicationContext 为核心的接口系列中,ListableBeanFactory 和HierarchicalBeanFactory 两个接口连接了 BeanFactory 接口定义和ApplicationConext 应用上下文的接口定义。
 

在 ListableBeanFactory 接口中,细化了许多 BeanFactory 的接口功能,比如定义了getBeanDefinitionNames 接口方法。对于 ApplicationContext 接口,它通过继承MessageSource、ResourceLoader、ApplicationEventPublisher 接口,在BeanFactory 的基础上添加了对高级容器特性的支持。

解读:

ApplicationContext 继承了 BeanFactory 接口,具有了容器的基本功能,同时根据上下文的特点,也用 ListableBeanFactory 接口做了功能扩展。上下文与容器的主要区别,还是体现在容器高级特性上,比如 MessageSource 实现了国际化、ResourceLoader 实现了资源加载、ApplicationEventPublisher 实现了事件机制。因此平时工作中使用上下文会多一点,一般不直接使用 BeanFactory 简单容器。

3.FactoryBean

在 BeanFactory 中,Bean 是通过 FactoryBean 来获取的。FactoryBean是一个工厂Bean,可以生成某一个类型 Bean 的实例,它最大的一个作用是:可以让我们自定义 Bean 的创建过程。可以使用转义符 “&” 得到 FactoryBean 本身,用来区分通过容器来获取 FactoryBean 产生的对象和获取 FactoryBean 本身。

解读:

FactoryBean 和 BeanFactory,一个是 Factory,也就是 IOC 容器工厂,一个是特色类型的 Bean。所有的 Bean 都是由 BeanFactory 管理。FactoryBean 是一个能产生或者修饰对象生成的工厂 Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。这两个类型名称比较接近,很多人容易混淆,只要记住结尾区分即可,一个是工厂,一个是 Bean。

4.BeanFactory容器的设计原理

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

解读:

这里的 XmlBeanFactory 是一个基于 XML 的容器实现。从类图可以看到,容器的实现也是分层的,每一层接口都有对应的实现,每一个实现都只做自己职责范围内的事情,通过继承形成了一个多层次的容器结构。如果我们要定义自己的容器实现,只需要像它一样按需继承和实现即可。一定要理解分层的意义,这样才能设计出更好的实现。

DefaultListableBeanFactory 实际上包含了基本IoC容器所具有的重要功能,在Spring中,实际上是把 DefaultListableBeanFactory 作为一个默认的功能完整的 IoC 容器来使用的。XmlBeanFactory 在继承了 DefaultListableBeanFactory 容器的功能的同时,增加了新的功能,是一个可以读取以 XML 文件方式定义的 BeanDefinition 的IoC容器。

解读:

在继承体系中,DefaultListableBeanFactory 实现了容器的重要功能。XmlBeanFactory 解决了 XML 文件的解析,并把解析出来的 Bean 定义注册到容器中。这就是一个逐层实现的设计,继承了默认的实现后,只需要根据自己的场景做定制即可,每一层的实现都不算复杂。

在 XmlBeanFactory 中,初始化了一个 XmlBeanDefinitionReader,用来读取以XML方式定义的 BeanDefinition。而 XML 作为资源文件,通过 Resource 类来封装 I/O 操 作。XmlBeanDefinitionReader 初始化后,调用 loadBeanDefinitions 方法从Resource 中载入 BeanDefinition。

解读:

一个真正完整的容器在启动阶段主要做几个事情:

  1. 找到 Bean 定义,如 xml、注解等,如果是资源文件可以用 Resource 类来封装,支持 ClassPath、jar、URL 等;

  2. 初始化 Reader 注入 Resource,BeanDefinitionReader 接口定义了解析相关的方法,Spring 默认提供了很多实现类;

  3. Reader 解析 BeanDefinition,初始化后注册到容器中。

5.ApplicationContext容器的设计原理

以常用的 FileSystemXmlApplicationContext 的实现为例。主要功能已经在AbstractXmlApplicationContext 中实现了,在 FileSystemXmlApplicationContext中,作为一个具体的应用上下文,只需要实现和它自身设计相关的两个功能。

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

解读:

refresh 是上下文的很重要的一个操作。Spring容器的启动,初始化一些容器启动必要的资源,BeanFactory 的创建、初始化,Bean 的创建、初始化、注册、非懒加载,注册和设置国际化工具类MessageSource,注册和设置事件,等一系列过程都在这个 refresh 方法里面进行调用。关于 refresh 的实现原理,后续会有文章解读,限于篇幅,这里就不展开了。

FileSystemXmlApplicationContext 是一个从文件系统加载 XML 的上下文实现,因此

设计了从文件系统中加载XML的功能。

解读:

可以看到,Spring 内部上下文的实现和继承关系非常复杂,难以理解。实际上,按照实现分层的思路去理解还是比较容易的,每一层只实现自己相关的功能。类似或者公用的能力都往下沉淀变为底层的基础能力,上层实现只做调用。看源码的时候,要有全局视野,哪些是公用能力,哪些是本层次定制功能,这样就会好理解一点。

猜你喜欢

转载自blog.csdn.net/qq_35571554/article/details/84785785