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

Spring IoC 容器的设计与实现原理相关推荐

  1. Spring IoC容器的设计—2—主线

    第二条接口设计主线是,以ApplicationContext应用上下文接口为核心的接口设计,这里涉及的主要接口设计有,从BeanFactory到ListableBeanFactory,再到Applic ...

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

    在写BeanFactory与ApplicationContext 之前,我想先简单聊一聊Spring IoC 容器,希望能给大家一个参考.如果你对这反面的知识比较了解,可以直接跳过. (一)Sprin ...

  3. Spring IoC容器设计与实现——IoC容器的依赖注入

    Spring IoC容器设计与实现--IoC容器的初始化过程 依赖注入的过程是用户第一次向IoC容器索要Bean时触发的,当然也有例外,就是可以通过控制lazy-init属性来让容器完成对Bean的预 ...

  4. spring bean加载过程_Spring源码剖析3:Spring IOC容器的加载过程

    本文转自五月的仓颉 https://www.cnblogs.com/xrq730 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https ...

  5. Spring – IoC 容器

    Spring IoC 容器是 Spring 框架的核心.容器将创建对象,将它们连接在一起,进行配置,并管理从创建到销毁的整个生命周期.Spring 容器使用依赖项注入(DI)来管理组成应用程序的组件. ...

  6. Spring IOC 容器源码分析

    Spring IOC 容器源码分析 创建时间: 2017-11-15 00:00:00 [TOC] Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring ...

  7. Spring IoC容器初始化源码(1)—容器初始化入口以及setConfigLocations设置容器配置信息【一万字】

      基于最新Spring 5.x,对于基于XML的Spring IoC容器初始化过程中的setConfigLocations设置容器配置信息方法的源码进行了详细分析,最后给出了比较详细的方法调用时序图 ...

  8. Spring IOC 容器源码分析系列文章导读

    1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...

  9. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  10. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

最新文章

  1. 判断出栈顺序是否正确(栈的压入、弹出序列)
  2. POJ1083 Moving Tables
  3. 三足鼎立 —— GPM 到底是什么?(一)
  4. CF1322B-Present【双指针】
  5. mysql :完整性约束
  6. 客座编辑:武永卫,男,博士,清华大学计算机科学与技术系教授。
  7. 静态路由(实验讲解+配置)
  8. python-33:极视界爬虫V-0.2
  9. 【通信原理 入坑之路】基于MATLAB的移动通信系统仿真 之 瑞利衰落信道的原理与仿真
  10. serialVersionUID 生成
  11. Windows套接字I/O模型(3) -- WSAAsyncSelect模型
  12. Scheme语言基础之数据类型
  13. ES6之什么是箭头函数?
  14. 学透for循环-传统for循环与增强for循环
  15. 桌面文件突然不见了怎么恢复?
  16. 绝对位置运动指令(MoveAbsJ)
  17. LaTeX调整公式中部分字号及行距
  18. c#串口模拟互发数据(COM1-COM2)
  19. 密码学之基本概念(01)
  20. IT人员求职招聘网站

热门文章

  1. H3CNE V7.0 视频教程
  2. Python-Scrapy 获取历史双色球开奖号码
  3. linux安装lsi raid卡驱动下载,【LSIRAID卡驱动下载】LSIRAID卡官方驱动程序下载
  4. gitlab 版本升级
  5. 早餐为啥不能吃大米粥?医生:不仅是米粥,这3物也最好少吃
  6. 电动机效率 matlab,【原创】matplotlib绘制电机效率MAP图
  7. 街头卖艺里的故事,你还会相信吗?
  8. 看两宋风云,搞清了四个之前对两宋历史认识错误的地方
  9. 4.3.2 Calculating and Applying VaR
  10. 采样频率和带宽的关系_示波器的带宽与采样率是什么关系