Spring 中的 IoC 容器

发布于 2022-4-24 09:05
浏览
0收藏

作者 |jaxer
来源 | WriteOnRead(ID:WriteOnRead)

概述

对于 Java 后端开发而言,Spring 框架的重要性不言而喻。而 Spring 中最核心的无非就是 IoC 和 AOP。

相关的概念不再赘述,网上可以找到很多对它们的介绍。

这里想说的是,IoC 只是一种设计思想,它的原理在不同语言有多种实现,同一种语言也有多个产品,Spring 是 Java 语言实现中最著名的一个。

刚开始接触 IoC 这个概念的时候对这些不是太清楚,以为是 Spring 独有的,后面发现并不是。

很多小伙伴平时的工作中只知道怎么去用,但不知道 Spring 是如何实现 IoC 的。本文以及接下来的几篇文章打算从源码的角度研究和分析一下 Spring IoC 的实现原理(AOP 要不要写后面再看心情吧)。

作为爱好学习的猿媛们,怎么能只知其一、不知其二呢?而且面试还是很有可能问到的哦。

Spring IoC 总览

描述

Spring IoC 主要是通过一系列的「容器」来实现的。

起初对「容器」这个概念不是很理解:听起来「容器」像是装东西的,但它究竟装的是什么东西、怎么装的呢?似乎还是有些抽象。

其实可以把「容器」类比成生活中常见的物品,比如水杯。水杯装的什么?当然是水。Spring 的 IoC 容器呢?装的就是 Java 对象。

Spring 中的容器主要可以分为两个系列:BeanFactory 系列和 ApplicationContext 系列。前者是最基本的容器,而后者是功能比较丰富的容器。

用水杯来打比方:可以认为 BeanFactory 只是一个最简单、最原始的水杯(甚至连把手都没有),而 ApplicationContext 是比较高级的水杯,比如有把手的水杯,或者保温杯等更高级的水杯。

继承结构

BeanFactory 的主要接口继承结构如下:Spring 中的 IoC 容器-开源基础软件社区

ApplicationContext 的主要接口继承结构如下:Spring 中的 IoC 容器-开源基础软件社区

可以看到 ApplicationContext 接口也继承自 BeanFactory。本来就是嘛!再高级的水杯它不还是个水杯?

那我们平时用的是哪种呢?

当然是高级的咯!有了法拉利,还要什么自行车!

ApplicationContext 的几个主要实现类,以及它们相对完整的继承结构如下(好像稍微有点复杂,其实看看就行,这几个图都是 Intellij IDEA 自动生成的,有兴趣也可以自己去搞一下):Spring 中的 IoC 容器-开源基础软件社区

ApplicationContext 比较常用的几个实现类有:

  • ClassPathApplicationContext
  • FileSystemXmlApplicationContext
  • XmlWebApplicationContext
  • AnnotationConfigApplicationContext
  • AnnotationConfigWebApplicationContext

下面以 ClassPathApplicationContext 为例进行分析(其实这几个实现类的原理都是类似的)。

代码演练

两个普通 Java 类

准备两个普通的 Java 类如下:

Person.java

public class Person {
    private int id;
    private String name;
    private Dog pet; 
    // 省略了 getter、setter、toString 方法    
}

Dog.java

public class Dog {
    private int age;
    private Person owner;
    // 省略了 getter、setter、toString 方法
}

Spring 配置文件

在 classpath 目录下创建一个 Spring 的配置文件 application-ioc.xml,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="person" class="com.jaxer.spring.ioc.Person">
        <property name="pet" ref="dog"/>
    </bean>
    <bean id="dog" class="com.jaxer.spring.ioc.Dog">
        <property name="age" value="1"/>
        <property name="owner" ref="person"/>
    </bean>
</beans>

PS: 这里悄悄埋了一个伏笔,就是常见的循环依赖问题,后面再来填坑。

测试类

public class IocTests {
    @Test
    public void test01() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-ioc.xml");
        System.out.println(context.getBean("person"));
        System.out.println(context.getBean("dog"));
    }
}
/*
 * 输出结果:
 *  Person{id=12, name='Jack-12'}
 *  Dog{age=1}
 */

这个例子比较简单,看起来我们把配置文件交给了 ClassPathXmlApplicationContext,就能从它那里拿到 Person 和 Dog 的对象,那它究竟做了什么呢?

IoC 相关流程和类

Spring IoC 的实现其实还是有些复杂的,当初看了好几遍还是云里雾里(可能是我智商不够)。

在分析实现原理之前,先大概了解下它的主要流程(先整体后局部,不是嘛?)。

主要流程

还是以水杯来做对比。如果我们想从水杯中取水,主要流程如下:

  1. 找到水源在哪里
  2. 把水装到杯子里
  3. 从水杯中取水

Spring IoC 容器跟这个过程是类似的。如果我们想从 Spring IoC 容器获取 Java 对象,那么:

  1. 找到对象在哪里(在哪里呢?扎心了)
  2. 把对象放入 IoC 容器
  3. 从 IoC 容器获取对象

哈哈,这里忽然想起了把大象放到冰箱有几个步骤,是不是有点像?

这三个步骤可以用 Spring 的术语描述如下:

  1. Resource 定位
  2. BeanDefinition 的载入和注册
  3. 依赖注入

Spring IoC 容器把它管理的对象称为 Bean,定义 Bean 的原始信息称为 Resource,BeanDefinition 是 Spring 内部保存对象定义的数据结构(就像 JVM 类加载后,方法区保存的类型信息那样)。

Resource 定位

Resource

Resource 是 Spring 对用户定义 Bean 文件的统一封装。

比如 FileSystemResource、ClassPathResource 等,有木有跟相应的 ApplicationContext 对应起来?

Resource 常见实现类的继承结构如下:Spring 中的 IoC 容器-开源基础软件社区

ResourceLoader

为了读取 Resource,Spring 还设计了 ResourceLoader,可真是个暖心 boy,不得不说 Spring 把面向对象(OOP)的设计思想表现的淋漓尽致,这只不过是冰山一角。

ResourceLoader 常见实现类的继承结构如下:Spring 中的 IoC 容器-开源基础软件社区

BeanDefinition 的载入、注册

BeanDefinition 载入

所谓的载入,其实就是把 Resource 中的内容解析出来,转换成 Spring 内部定义的数据结构 BeanDefinition。

比如上面的 application-ioc.xml 文件,配置了我们定义的 Java 类型信息,Spring 要读取和解析该文件,并把类信息转换为 BeanDefinition。是不是有点像 JVM 的类加载?

BeanDefinition 的常见实现类和继承结构如下:Spring 中的 IoC 容器-开源基础软件社区

与之对应呢,BeanDefinition 也有 BeanDefinitionReader:Spring 中的 IoC 容器-开源基础软件社区

BeanDefinition 注册

BeanDefinition 的注册其实比较简单,就是把前面已经载入的 BeanDefinition 注册到注册中心 BeanDefinitionRegistry。

直白一点,其实就是把 BeanDefinition 放入注册中心的 Map 中。

依赖注入

其实就是从 Spring IoC 容器获取对象的过程。

这个过程说起来简单,其实还是有些复杂的……后面再慢慢聊。

小结

本文先从整体上介绍 Spring IoC 以及主要接口的继承体系,后面再进一步分析它的实现原理。

啊啊啊!写到这里脑汁又被挤干净了……需要一段时间才能恢复元气。

收藏
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐