Spring IoC(一)IoC容器的设计与实现:BeanFactory与ApplicationContext
在写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);
过程分析:
- 创建IoC 配置文件的抽象信息,这个抽象资源包含了BeadDefinition 的定义信息。
- 创建一个BeanFactory,这里以DefaultListableBeanFactory 为例。
- 创建一个BeadDefinition 的读取器,通过一个回调配置给BeanFactory。
- 从定义好的资源位置读取配置信息,具体的解析过程由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相关推荐
- Spring 的IOC容器系列的设计与实现:BeanFactory 和 ApplicationContext
在Spring IOC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本的功能,另一个是ApplicationContex ...
- Spring - Java/J2EE Application Framework 应用框架 第 3 章 Beans, BeanFactory和ApplicationContext
第 3 章 Beans, BeanFactory和ApplicationContext 3.1. 简介 在Spring中,两个最基本最重要的包是 org.springframework.beans 和 ...
- 【Spring】IoC容器系列的设计与实现:BeanFactory和ApplicationContext
在Spring IoC容器的设计中,我们可以看到两个主要的容器系列,一个是实现BeanFactory接口的简单容器系列,这系列容器只实现了容器的最基本功能,另一个是ApplicationContext ...
- Spring IoC容器的设计—2—主线
第二条接口设计主线是,以ApplicationContext应用上下文接口为核心的接口设计,这里涉及的主要接口设计有,从BeanFactory到ListableBeanFactory,再到Applic ...
- 抛开 Spring 去理解 IOC 思想:原来 IOC 容器这么简单
很多小伙伴们看到标题可能就会想到抛开 Spring 就不会存在 IOC 思想了,其实不然在接下来的文章中就会讲述到. 很多小伙伴在理解 IOC 的时候通常会和 Spring 放到一起去学习,首先呢 S ...
- Spring IoC(二)IoC容器的初始化过程
(一)IoC 容器初始化过程概述 1.1简要概述初始化过程 IoC 容器的初始化过程是通过refresh() 方法来启动的,这个方法标识着IoC 容器正式启动.具体来说,这个启动过程包括:BeanDe ...
- ioc spring 上机案例_抛开Spring去理解IOC思想 - 原来IOC容器这么简单
很多小伙伴们看到标题可能就会想到抛开Spring就不会存在IOC思想了,其实不然在接下来的文章中就会讲述到. 很多小伙伴在理解IOC的时候通常会和Spring放到一起去学习,首先呢Spring设计的非 ...
- Spring中的IOC容器原理
什么是IOC: IOC(Inverse of Control)控制反转,将对象的创建权利反转给Spring框架. 它是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度. IOC容器的底 ...
- Spring技术内幕——Spring Framework的IOC容器实现(一)
一.SpringIOC容器概述 IOC容器和依赖反转的模式 在面向对象的系统中,对象封装了数据和对数据的处理,对象的依赖关系常常体现在对数据和方法的依赖上.这些依赖关系可以通过把对象的依赖注入交给框架 ...
最新文章
- Ubuntu之Gitlab、Gerrit、Jenkins协调工作配置
- sqlserver结果集转为字符串
- Django视图简介
- NeurIPS2021 HRFormer:HRNet又出续作啦!国科大北大MSRA提出高分辨率Transformer,开源!...
- 蒲公英如何正确泡水喝?可以和哪些食物一起搭配?
- PHP可以读取什么配置文件,PHP读取配置文件类实例
- python全栈 操作系统
- Permission denied (publickey) 解决方案
- Java加密与解密笔记(二) 对称加密
- 罗马尼亚:曾经的黑客避风港变身全球安全人才的摇篮
- 【tensorRT文档翻译】7. Working With Dynamic Shapes
- 十大排序算法——选择排序法
- 如何使用SqlLoader导入数据
- C语言极速学习开发——51单片机入门编程之使用KeilC51进行代码编译(点亮你心中学习的精神之灯-上)
- 微信小程序UI组件库合集
- php集成环境安装包网盘,一键安装PHP环境(Z Serv)PHP集成环境安装包
- Linux环境安装Postgresql报错。configure: error: zlib library not found
- 计算机进入安全模式的原因,电脑只能进入安全模式的原因及处理方法
- 提升自我的42个实用技巧
- Simulink-模块Moudle调用回调函数步骤
热门文章
- java向指定文件写入内容
- Element UI——数字输入框解决方案
- Splitting into digits
- 相关疑惑解决,java线程虚假唤醒等等问题
- Android复习04(适配器 Get()请求 适配器 getView()方法 Post()请求 保存Cookie 流转字符串 从网上获取图片 重点考Json解析)
- Android ConstraintLayout ConstraintSet动态布局
- IOS 开发一些常用的地址
- 网页性能优化04-函数节流
- Java虚拟机是什么
- 《剑指offer》-- 斐波那契数列、跳台阶问题 、变态跳台阶问题、矩阵覆盖