IoC容器的实现学习——01

简介

在以前通常情况下一个简单的项目一般由两个及两个以上的类构成,大多数的类集数据和数据的处理方法于一体,类之间通过依赖彼此的数据和方法实现业务逻辑,这个获取依赖的过程是自己实现的,导致代码高度耦合以及难以测试。

所以出现了DI (依赖注入)、IoC (控制反转) 这些将对象的依赖关系转交给平台或容器进行管理的设计模式,而在 Spring 核心中 IoC 容器就是这种模式的实现。通过将对象的依赖控制交给 IoC 容器从而有效降低代码的耦合度,提高代码的可测试性。

IoC 容器需要解决的核心问题是如何将对象的控制权从对象转交给平台或框架中。

IoC 核心思想是关于一个对象如何获取它所依赖的对象引用。

这次除了对 IoC 的简单回顾,还有对 Spring 框架实现的 IoC 容器进行设计和实现上的分析,深入了解一下 Spring IoC 独特的特点。

IoC 容器系列的设计与实现:BeanFactory 和 ApplicationContext

BeanFactory 接口系列:只实现了容器的基本功能。

ApplicationContext 应用上下文系列:在简单容器的基础上增加了许多面向框架的特性,同时对不同的应用场景进行了适配。

下面对其两者进行逐一分析。

BeanFactory

基本概念:BeanFactory 接口定义了一个 IoC 容器所应该具备的最基本服务,同时也是我们使用 IoC 容器遵守的最底层和最基本的编程规范。

同时有许多的类实现了 BeanFactory 这个基本接口,并在其基础上进行了适当的扩展形成了具体的 IoC 容器,以针对不同的场景供用户选择,其中 ApplicationContext 也是在其基础上构造的。

BeanFactory 接口的源码:

这些规范,体现了 IoC 容器最基本的特性。

其中主要的方法就是 getBean() 方法,还有一些其他的检索方法,是最为基本的容器入口。

可以通过分析一个基本的 IoC 容器—— XmlBeanFactory 实现,来尝试理解简单 IoC 容器的设计原理。

XmlBeanFactory 使用了 DefaultListableBeanFactory 作为基类,两者作为基本的 IoC 容器,通过观察 XBF 这个类名,可以猜到大概是通过 XML 文件来解析 BeanDefinition,并初始化 IoC 容器(事实也是如此)。

可以看到有一个常量,XmlBeanDefinitionReader 这个类是用来解析处理以 XML 形式定义的 BeanDefinition,是 Reader 对象,但是并不是它来获取 XML 资源,XML 资源是交由另一个对象(Resource)来获取并抽象的。Resource 是 Spring 用来封装 I/O 操作的类(称为定位 BeanDefinition 资源),例如 ClassPathResource 这个类可以指定获取到具体的资源,并且将 Resuorce 交由 XBF 的构造函数,这样 IoC 容器就可以方便的定位到需要的 BeanDefinition 信息对 Bean 完成容器的初始化和 DI 过程。

可能思路还不是很清晰,接下来就简单解释一下一些类:

  • BeanDefinition:

    这个类是 Spring 为了方便 IoC 容器管理 POJO (Bean) 对象而对其进一步抽象的数据结构,根据资源所定义的信息来创建具体的 Bean 对象。

    假设我们通过 XML 的形式来让 IoC 容器帮我们管理 Bean 对象,其实我们在 XML 中定义的是 BeanDefinition,我们通过 <bean/> 标签来创建 Bean,实际上是在描述一个 BeanDefinition,而当 IoC 容器通过 Reader 定位到 需要的 BeanDefinitieon,则是根据 Reader 解析好的 BeanDefinition 信息来创建我们描述好的 Bean 对象,并进行管理。

  • BeanDefinitionReader:

    读取 Spring 配置文件中的内容,并将其转换为 IoC 容器的内部数据结构:BeanDefinition。

    在 XBF 中 XBDR 就是 BDR 的一个具体实现,Spring 的配置内容是 XML,所以根据 XML 的形式来转换 BeanDefinition。

  • Resource:

    Spring 统一对不同来源的资源进行底层抽象,方便统一管理不同的资源。

    通过简单的观察继承关系和部分源码,大概分析是 Spring 对不同来源的资源统一进行底层资源封装或抽象。

    再通过不同的实现类对具体的资源面向用户提高具体的服务接口。

通过解释或者百度几个关键类,我们应该能理解 IoC 容器初始化的简单过程。

观察 XBF 的源码可以发现除了使用 XBDR 对象进行资源的解析和加载,并没有看到关于 IoC 容器的初始化过程,看到 super(parentBeanFactory) 这句代码调用了父类 —— DefaultListableBeanFactory 的构造方法,从而完成了 IoC 容器的构建。那么我们初步可以分析出 DefaultListableBeanFactory 应该是 XBF IoC 容器创建的重要对象。实际上它也是个基本 IoC 容器。

直到这里我们就简单分析出了 XBF IoC 容器的创建过程。

loadBeanDefinition()

通过 reader 解析好的 Bean 信息加载到 BeanFactory (IoC 容器)中。

ApplicationContext

对于开发人员来说,例如开发一个 Web 服务端,如果要开发人员手动控制 Bean 的配置和容器的建立过程,无疑是非常痛苦的,所以 Spring 帮我们定义了许多已经实现好的容器,并且这些容器面向的需求也不一样,相对于已经实现的简单 BeanFactory 容器,不能很大程度上满足开发人员的需求,所以 ApplicationContext 无疑是更好的选择。

之前介绍了 ApplicationContext 是在基本 IoC 容器上,进行了更大程度的扩展,让 IoC 容器面向框架,提供更多的服务,方便开发人员的使用,更加专注于业务逻辑的实现。同时也是对 IoC 容器一次全面的更新和扩展。

AC 扩展了一些接口,在基础 IoC 容器上添加了附加功能,这些额外的功能为 AC 提供了 BeanFactory 不具备的特性:

  • 支持不同的信息源:扩展了 MessageSource 接口,支持国际化,为开发多语言版本的应用提供服务。

  • 访问资源:主要体现在对 ResourceLoader 和 Resource 的支持上,让我们可以获从不同的地方获取 Bean 资源,主要是可以在不同的 I/O 途径获取 Bean 定义信息。这里的指的是具体的 ApplicationContext 容器,一般来说都是继承了 DefaultResourceLoader 的子类,因为 DefaultResourceLoader 是 AbstractApplicationContext 的基类。

  • 支持应用事件

    继承了接口 ApplicationEventPublisher,从而在上下文中引入了事件机制,这些事件结合 Bean 的生命周期对 Bean 管理提供了便利。

  • 提供其他附加服务

    这些其他的附加服务,使得基本的 IoC 的功能更加丰富,使它的使用是一种面向框架 的使用风格。

设计原理:

以常用的 FileSystemXmlApplicationContext 的实现为例说明 ApplicationContext 容器的设计原理。

通过观察 FSXAC 的源码,AC 容器的主要功能已经在 FSXAC 的基类 AbstractXmlApplicationContext 中实现了,所以 FSXAC 只要实现与自身设计相关的两个功能。

功能一:如果直接使用 FSXAC,对于实例化这个应用上下文的支持,同时启动 IoC 容器的 refresh() 过程。

refresh() 过程牵涉到 IoC 容器启动的一系列复杂操作,对于不同的容器,这些操作都是类似的,因此在基类(AbstractApplicationContext)中对其统一封装。

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
}
}

功能二: 如何从文件系统中加载 XML 的 Bean 定义资源有关。

简单来说就是如何在文件系统中读取以 XML 形式存在的 BeanDefinition 做准备(并不是直接解析),因为不同的 AC 实现对应着不同的读取 BeanDefinition 的方式。

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

上面是这个功能的实现,可以看到,调用这个方法可以得到 Resource 资源定位——FileSystemResource

小结

本次学习了 IoC 容器的一些介绍和概念,抓住了在 Spring 中 IoC 容器的两大实现方式:BeanFactory 和 ApplicationContext.

借助常用或典型的实现类:XmlBeanFactoryFileSystemApplicationContext

通过简单分析两者的实现和设计区别,来尝试理解两者本质或定义上的区别,同时我们也在两者的实现和设计中,学到了 Spring IoC 容器中基础的组成部分:

  • BeanDefinition
  • BeanDefinitionReader
  • Resource
  • DefaultListableBeanFactory
  • loadBeanDefinitions() 方法

这些与 IoC 容器密切相关。


在平时的 Spring 学习和使用中,我们大部分都仅限于使用,有时候项目报错,即使百度,也会出现不能快速定位或者解决了但是又没完全解决(不理解)的情况。

除了收集我们的日常 BUG 外,我们还需要对其浅层原理进行理解。我想这应该是逃离“码农”的一小步。

努力地靠近自己理解的正轨。

Spring 深入——IoC 容器 01的更多相关文章

  1. Spring框架IOC容器和AOP解析

    主要分析点: 一.Spring开源框架的简介  二.Spring下IOC容器和DI(依赖注入Dependency injection) 三.Spring下面向切面编程(AOP)和事务管理配置  一.S ...

  2. Spring之IOC容器加载初始化的方式

    引言 我们知道IOC容器时Spring的核心,可是如果我们要依赖IOC容器对我们的Bean进行管理,那么我们就需要告诉IOC容易他需要管理哪些Bean而且这些Bean有什么要求,这些工作就是通过通过配 ...

  3. spring框架--IOC容器,依赖注入

    思考: 1. 对象创建创建能否写死? 2. 对象创建细节 对象数量 action  多个   [维护成员变量] service 一个   [不需要维护公共变量] dao     一个   [不需要维护 ...

  4. 在Servlet(或者Filter,或者Listener)中使用spring的IOC容器

    web.xml中的加载顺序为:listener > filter > servlet > spring. 其中filter的执行顺序是filter-mapping在web.xml中出 ...

  5. Spring的IoC容器

    Spring是一个轻量级的Java开发框架,其提供的两大基础功能为IoC和AOP,其中IoC为依赖反转(Inversion of Control).IOC容器的基本理念就是"为别人服务&qu ...

  6. Spring的IOC容器第一辑

    一.Spring的IOC容器概述 Spring的IOC的过程也被称为依赖注入(DI),那么对象可以通过构造函数参数,工厂方法的参数或在工厂方法构造或返回的对象实例上设置的属性来定义它们的依赖关系,然后 ...

  7. SpringMVC系列(十五)Spring MVC与Spring整合时实例被创建两次的解决方案以及Spring 的 IOC 容器和 SpringMVC 的 IOC 容器的关系

    一.Spring MVC与Spring整合时实例被创建两次的解决方案 1.问题产生的原因 Spring MVC的配置文件和Spring的配置文件里面都使用了扫描注解<context:compon ...

  8. spring的Ioc容器与AOP机制

    为什么要使用Spring的Ioc容器? 1.首先,spring是一个框架,框架存在的目的就是给我们的编程提供简洁的接口,可以使得我们专注于业务的开发,模块化,代码简洁,修改方便. 通过使用spring ...

  9. Spring扩展:Spring的IoC容器(注入对象的方式和编码方式)

    二.Spring的IoC容器 IoC:Inversion of Control(控制反转) DI:Dependency Injection(依赖注入) 三.依赖注入的方式 (1)构造注入 (2)set ...

随机推荐

  1. B.E.M 规范

    BEM文档 BEM: A New Front-End Methodology 如何看待 CSS 中 BEM 的命名方式? Battling BEM CSS: 10 Common Problems An ...

  2. 微信公众号授权登录后报redirect_uri参数错误的问题

      在进行微信公众号二次开发的时候,需要通过授权码模式来进行微信授权.比如,在进行登录的时候,用户点击了登录按钮,然后弹出一个授权框,用户点击同意后,就可以获取用户的OpenId等信息了.这篇文章主要 ...

  3. Servlet特性研究之异步模式

    Servlet只有同步模型是怎样的? 异步处理是Servlet3.0版本的重要功能之一,分析异步处理模型之前,先看看同步处理的过程是怎样的: 客户端发起HTTP请求一个动态Servlet API,请求 ...

  4. 从零开始搭建react基础开发环境(基于webpack5)

    前言 最近利用闲暇时间把webpack系统的学习了下,搭建出一个react环境的脚手架,写篇文章总结一下,帮助正在学习webpack小伙伴们,如有写的不对的地方或还有可以优化的地方,望大佬们指出,及时 ...

  5. 【三维地图】开发攻略 —— 详解“GeoJSON”技术和应用场景

    GeoJSON ,一个用于存储地理信息的数据格式.GoeJSON对象可以表示几何.特征或特征集合,支持:点.线.面.多点.多线.多面和几何集合.在基于平面地图,三维地图中都需要用到的一种数据类型. 由 ...

  6. django_day07

    django_day07 django form组件 form组件的定义 class RegForm(forms.Form): user = forms.CharField(label='用户名') ...

  7. 第八十四篇:Vue购物车(五) 商品数量的增减

    好家伙, 1.商品数量的增减 我们把商品的数量增减独立出来,写成一个独立的组件Counter <template> <div class="number-container ...

  8. 大数据Hadoop平台安装及Linux操作系统环境配置

    配置 Linux 系统基础环境 查看服务器的IP地址 设置服务器的主机名称 hostnamectl set-hostname hadoop hostname可查看 绑定主机名与IP 地址 vim /e ...

  9. Windows平台RTMP/RTSP播放器实现实时音量调节

    为什么要做实时音量调节 RTMP或RTSP直播播放音量调节,主要用于多实例(多窗口)播放场景下,比如同时播放4路RTMP或RTSP流,如果音频全部打开,几路audio同时打开,可能会影响用户体验,我们 ...

  10. v-if和v-for的优先级是什么?

    一.作用 v-if 指令用于条件性地渲染一块内容.这块内容只会在指令的表达式返回 true值的时候被渲染 v-for 指令基于一个数组来渲染一个列表.v-for 指令需要使用 item in item ...