Spring-IoC-Spring-IoC-的设计

18次阅读

共计 3032 个字符,预计需要花费 8 分钟才能阅读完成。

前言

本文为解读 Spring IoC 模块源码的开篇介绍。介绍 Spring IoC 的相关概念与设计。

What is IoC

控制反转(Inversion of Control,缩写为 IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称 DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递 (注入) 给它。

— 摘自维基百科

大型应用中,需要多个类组合工作来实现业务逻辑。这使得每个对象都需要在工作的时候获取到与其合作的对象的引用。

如果这个获取过程要靠自身来实现,那么,代码会变得高度耦合并且难以测试。这对复杂的 OOP 系统的设计是非常不利的。

在 OOP 系统中,对象封装了数据和对数据的处理动作,对象的依赖关系体现在了对数据和方法的依赖上。这些依赖关系,可以通过把对象的依赖注入交给框架或 IoC 容器来完成。

简单来说:

  • 控制:当前对象对其内部成员对象的控制权 / 获取组装对象的过程
  • 反转:上述的过程 / 控制权,交由专门的第三方组件(容器或者说平台)来管理

这种从具体对象手中,交出控制的做法,在解耦代码的同时提高了代码的可测试性。好处具体如下:

  1. 不用自己组装,拿来就用。
  2. 享受单例的好处,效率高,不浪费空间。
  3. 便于单元测试,方便切换 mock 组件。
  4. 便于进行 AOP 操作,对于使用者是透明的。
  5. 统一配置,便于修改。

Spring IoC

在 Spring 中,IoC 容器是实现这个模式的载体,它可以在对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入

就此而言,这种方案有一种完整而简洁的美感,它把对象的依赖关系有序地建立起来,简化了对象依赖关系的管理,在很大程度上简化了面向对象系统的复杂性。

Spring IoC 提供了一个基本的 JavaBean 容器,通过 IoC 模式管理依赖关系,并通过依赖注入和 AOP 切面增强了为 JavaBean 这样的 POJO 对象赋予事务管理、生命周期管理等基本功能。

IoC 容器的设计

在 Spring IOC 容器的设计当中,我们可以看到两个主要的容器系列(根据命名),

  • 实现了 BeanFactory 接口的简单容器系列,只实现了容器的最基本功能;
  • ApplicationContext 应用上下文,容器的高级形态,增加了许多面向框架的特性和对应用环境的适配;

对于使用者来说,这些都是容器,是容器的不同表现形式,使用什么样的容器完全取决于使用者的需求。

BeanFactory

BeanFactory 接口定义了 IoC 容器最基本的形式,并且提供了 IoC 容器所应该遵守的最基本的服务契约,同时,这也是我们使用 IoC 容器所应遵守的最底层和最基本的编程规范,这些接口定义勾画出了 IoC 的基本轮廓。

往下的整个继承树是蛮复杂的,你也不需要所有都掌握,就想我们之前提过的怎么学习源码,找核心类。现在来挑几个重点的 BeanFactory 说一下。避免后面源码分析章节会一脸懵逼。

  • ListableBeanFactory,这个 Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,大家看源码会发现,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。
  • ApplicationContext 继承了 HierarchicalBeanFactory,Hierarchical 单词本身已经能说明问题了,也就是说我们可以在应用中起多个 BeanFactory,然后可以将各个 BeanFactory 设置为父子关系。
  • AutowireCapableBeanFactory 这个名字中的 Autowire 大家都非常熟悉,它就是用来自动装配 Bean 用的,但是仔细看上图,ApplicationContext 并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。
  • DefaultListableBeanFactory,在 Spring 中,实际上是把 DefaultListableBeanFactory 作为一个默认的功能完整的 IoC 容器来使用的。包含了基本 IoC 容器所具有的重要功能。

以上接口,推荐大家去阅读一下他们的 JavaDoc,来了解作者是怎么描述的。更为准确。

ApplicationContext

ApplicationContext 是一个高级形态意义的 IoC 容器,ApplicationContext 在 BeanFactory 的基础上集成了 MessageSource, ApplicationEventPublisher, ResourcePatternResolver 这几个接口,这些接口为 ApplicationContext 提供了以下 BeanFactory 不具备的新特性:

  • 支持不同的信息源。我们看到 ApplicationContext 扩展了 MessageSource 接口,这些信息源的扩展功能可以支持国际化的实现,为开发多语言版本的应用提供服务。
  • 访问资源。这一特性体现在对 ResourceLoader 和 Resource 的支持上,这样我们可以从不同地方得到 Bean 定义资源。这种抽象使用户程序可以灵活地定义 Bean 定义信息,尤其是从不同的 I / O 途径得到 Bean 定义信息。
  • 支持应用事件。继承了接口 ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和 Bean 的生命周期的结合为 Bean 的管理提供了便利。
  • 在 ApplicationContext 中提供的附加服务。这些服务使得基本 IoC 容器的功能更丰富。因为具备了这些丰富的附加功能,使得 ApplicationContext 与简单的 BeanFactory 相比,对它的使用是一种面向框架的使用风格,所以一般建议在开发应用时使用 ApplicationContext 作为 IoC 容器的基本形式。

BeanDefinition

在这些 Spring 提供的基本 IoC 容器的接口定义和实现的基础上,Spring 通过定义 BeanDefinition 来管理基于 Spring 的应用中的各种对象以及它们之间的相互依赖关系。

BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。

BeanDefinition 抽象了我们对 Bean 的定义,是让容器起作用的主要数据类型。对 IoC 容器来说,BeanDefinition 就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个 BeanDefinition 的处理来完成的。

这些 BeanDefinition 就像是容器里装的水,有了这些基本数据,容器才能够发挥作用。

结语

接下去的章节我们会正式开始分析 Spring IoC 的具体实现,容器的初始化和依赖注入是怎么实现的。大部分都是源码解读的内容。

如果之前没有阅读源码经验的同学,可以先看看这篇你为什么要看源码?如何看源码?

参考

  • 《Spring 技术内幕》
  • https://javadoop.com/post/spr…
正文完
 0