在写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 继承自DefaultListableBeanFactory ,后者这个类非常重要,是经常可以用到的一个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);

过程分析:

  1. 创建IoC 配置文件的抽象信息,这个抽象资源包含了BeadDefinition 的定义信息。
  2. 创建一个BeanFactory,这里以DefaultListableBeanFactory 为例。
  3. 创建一个BeadDefinition 的读取器,通过一个回调配置给BeanFactory。
  4. 从定义好的资源位置读取配置信息,具体的解析过程由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 资源。下面是相关源码:

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

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

参考资料
《Spring 技术内幕》

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

  1. Spring 的IOC容器系列的设计与实现:BeanFactory 和 ApplicationContext

    在Spring IOC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本的功能,另一个是ApplicationContex ...

  2. Spring - Java/J2EE Application Framework 应用框架 第 3 章 Beans, BeanFactory和ApplicationContext

    第 3 章 Beans, BeanFactory和ApplicationContext 3.1. 简介 在Spring中,两个最基本最重要的包是 org.springframework.beans 和 ...

  3. 【Spring】IoC容器系列的设计与实现:BeanFactory和ApplicationContext

    在Spring IoC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本功能,另一个是ApplicationContext ...

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

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

  5. 抛开 Spring 去理解 IOC 思想:原来 IOC 容器这么简单

    很多小伙伴们看到标题可能就会想到抛开 Spring 就不会存在 IOC 思想了,其实不然在接下来的文章中就会讲述到. 很多小伙伴在理解 IOC 的时候通常会和 Spring 放到一起去学习,首先呢 S ...

  6. Spring IoC(二)IoC容器的初始化过程

    (一)IoC 容器初始化过程概述 1.1简要概述初始化过程 IoC 容器的初始化过程是通过refresh() 方法来启动的,这个方法标识着IoC 容器正式启动.具体来说,这个启动过程包括:BeanDe ...

  7. ioc spring 上机案例_抛开Spring去理解IOC思想 - 原来IOC容器这么简单

    很多小伙伴们看到标题可能就会想到抛开Spring就不会存在IOC思想了,其实不然在接下来的文章中就会讲述到. 很多小伙伴在理解IOC的时候通常会和Spring放到一起去学习,首先呢Spring设计的非 ...

  8. Spring中的IOC容器原理

    什么是IOC: IOC(Inverse of Control)控制反转,将对象的创建权利反转给Spring框架. 它是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度. IOC容器的底 ...

  9. Spring技术内幕——Spring Framework的IOC容器实现(一)

    一.SpringIOC容器概述 IOC容器和依赖反转的模式 在面向对象的系统中,对象封装了数据和对数据的处理,对象的依赖关系常常体现在对数据和方法的依赖上.这些依赖关系可以通过把对象的依赖注入交给框架 ...

最新文章

  1. Ubuntu之Gitlab、Gerrit、Jenkins协调工作配置
  2. sqlserver结果集转为字符串
  3. Django视图简介
  4. NeurIPS2021 HRFormer:HRNet又出续作啦!国科大北大MSRA提出高分辨率Transformer,开源!...
  5. 蒲公英如何正确泡水喝?可以和哪些食物一起搭配?
  6. PHP可以读取什么配置文件,PHP读取配置文件类实例
  7. python全栈 操作系统
  8. Permission denied (publickey) 解决方案
  9. Java加密与解密笔记(二) 对称加密
  10. 罗马尼亚:曾经的黑客避风港变身全球安全人才的摇篮
  11. 【tensorRT文档翻译】7. Working With Dynamic Shapes
  12. 十大排序算法——选择排序法
  13. 如何使用SqlLoader导入数据
  14. C语言极速学习开发——51单片机入门编程之使用KeilC51进行代码编译(点亮你心中学习的精神之灯-上)
  15. 微信小程序UI组件库合集
  16. php集成环境安装包网盘,一键安装PHP环境(Z Serv)PHP集成环境安装包
  17. Linux环境安装Postgresql报错。configure: error: zlib library not found
  18. 计算机进入安全模式的原因,电脑只能进入安全模式的原因及处理方法
  19. 提升自我的42个实用技巧
  20. Simulink-模块Moudle调用回调函数步骤

热门文章

  1. java向指定文件写入内容
  2. Element UI——数字输入框解决方案
  3. Splitting into digits
  4. 相关疑惑解决,java线程虚假唤醒等等问题
  5. Android复习04(适配器 Get()请求 适配器 getView()方法 Post()请求 保存Cookie 流转字符串 从网上获取图片 重点考Json解析)
  6. Android ConstraintLayout ConstraintSet动态布局
  7. IOS 开发一些常用的地址
  8. 网页性能优化04-函数节流
  9. Java虚拟机是什么
  10. 《剑指offer》-- 斐波那契数列、跳台阶问题 、变态跳台阶问题、矩阵覆盖