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. 解气!哈工大被禁用MATLAB后,国产工业软件霸气回击

    提起哈尔滨工业大学,相信很多人都不会陌生. 它是中国顶级的C9院校之一,从1920年建校的百余年来,哈工大一直享誉"工科强校"的美称,因其在航天领域的不凡成就,更是被人们誉为&qu ...

  2. 整数分解和for循环

    整数的分解: 一个整数是由多位数字组成的,那么如何能分解出整数的各个位上的数字呢 对一个整数做%10的操作,就可以得到它的个位数 对一个整数做/10的操作,就去掉了他的个位数 然后再对2的结果做%10 ...

  3. Nuget打包并上传教程

    一.准备 1 . 下载 Download NuGet.exe 2 . windows 系统下设置环境变量 path中 或者 在dos 命令窗口下cd转到 nuget.exe 所在目录,这里为了每次使用 ...

  4. 初识Sentinel--雪崩问题及其解决方法

    什么是雪崩问题? 雪崩问题:微服务调用链中的某个服务故障,引起整个链路中的所有微服务不可用. 解决雪崩问题的常见四种方式: ①超时处理:设定超时时长,请求超过一定时间没有响应就返回错误信息,不会无休止 ...

  5. 后端Python3+Flask结合Socket.io配合前端Vue2.0实现简单全双工在线客服系统

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_158 在之前的一篇文章中:为美多商城(Django2.0.4)添加基于websocket的实时通信,主动推送,聊天室及客服系统,详 ...

  6. 利用CSS3自定义属性来为网站添加“暗黑模式”(暗色模式/DarkMode)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_114 究竟什么是暗黑模式?这个概念起初来源于macOS系统,该系统的mojave版本为用户提供两个主题皮肤,即浅色和深色的皮肤.自 ...

  7. .NET 纯原生实现 Cron 定时任务执行,未依赖第三方组件

    常用的定时任务组件有 Quartz.Net 和 Hangfire 两种,这两种是使用人数比较多的定时任务组件,个人以前也是使用的 Hangfire ,慢慢的发现自己想要的其实只是一个能够根据 Cron ...

  8. session,cookie,jwt的简单使用

    cookie的使用 https://blog.csdn.net/qq_58168493/article/details/122492358 session的使用 https://blog.csdn.n ...

  9. 【Java】学习路径62-枚举类型

    public enum Role { TEACHER,STUDENT,CHEF } 使用: System.out.println(r1);//输出TEACHER System.out.println( ...

  10. 深入分析JVM执行引擎

    程序和机器沟通的桥梁 一.闲聊 相信很多朋友在出国旅游,或者与外国友人沟通的过程中,都会遇到语言不通的烦恼.这时候我们就需要掌握对应的外语或者拥有一部翻译机.而笔者只会中文,所以需要借助一部翻译器才能 ...