【Spring源码】深入理解Spring IoC

一开始学习Spring的时候就接触IoC了,作为Spring第一个最核心的概念,我们在解读它的源码之前必须要对其有深入的认识,本篇主要介绍IoC基本概念和各个组件。

本文主要基于 Spring 5.0.6.RELEASE

IoC介绍

IoC全称为Inversion of Control即“控制反转”,它还有一个别名DI(Dependency Injection),即“依赖注入”。

如何理解“控制反转”?关键在于我们需要回答下面四个问题:

  1. 谁控制谁
  2. 控制什么
  3. 为什么是反转
  4. 哪些方面反转了

回答这四个问题前,我们先看IoC的定义:

所谓IoC,就是由Spring IoC容器来负责对象的生命周期和对象之间的关系。

这句话是IoC理论的核心,如何来理解这句话?我们引用一个例子来阐述。

找女朋友,一般情况下是如何找的呢?首先我们要根据自己的喜好(漂亮、身材好、性格好)找一个妹子,然后到处打听她的兴趣爱好、电话号码,然后各种投其所好追到手。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 年轻小伙子
*/
public class YoungMan {

private BeautifulGirl beautifulGirl;

YoungMan(){
// 可能你比较厉害,指腹为婚
// beautifulGirl = new BeautifulGirl();
}

public void setBeautifulGirl(BeautifulGirl beautifulGirl) {
this.beautifulGirl = beautifulGirl;
}

public static void main(String[] args){
YoungMan you = new YoungMan();
BeautifulGirl beautifulGirl = new BeautifulGirl("你的各种条件");
beautifulGirl.setxxx("各种投其所好");
// 然后你有女票了
you.setBeautifulGirl(beautifulGirl);
}
}

这就是我们通常做事的方式,如果我们需要某个对象,一般都是采用这种直接创建的方式(new BeautifulGirl()),这个过程复杂而又繁琐,而且我们必须要面对每个环节,在使用完成之后还需要复杂地销毁它,这种情况下我们的对象与它所依赖的对象耦合在一起。

其实我们需要思考一个问题,我们每次用到自己依赖的对象真的需要自己去创建吗?我们知道,我们依赖的对象其实并不是依赖该对象本身,而是依赖它所提供的服务,只要在我们需要它的时候,它能够及时提供服务即可,至于它是我们主动去创建的还是别人送给我们的,其实并不是那么重要。

这个给我们送东西的“人”就是IoC,在上面的例子中,它就相当于一个婚介公司,作为一个婚介公司它管理者很多男男女女的资料,当我们需要一个女朋友的时候,直接跟婚介公司提出我们的需求,婚介公司则会根据我们的需求提供一个妹子给我们,我们只需要负责谈恋爱就行了。

作为婚介公司的IoC帮我们省略了找女朋友的繁琐过程,将原来的主动寻找变成了现在的被动接受,更加简介轻便。原来需要各种鞍前马后亲力亲为的事,现在直接有人把现成的送过来,多么美妙的事情啊。所以,IoC的理念就是让别人为你服务,如下图(摘自Spring揭秘):

在没有引入IoC的时候,被引入的对象直接依赖于被依赖的对象,有了Ioc后,两者及他们的关系都是通过Ioc Service Provider来统一管理维护的。被注入的对象需要什么,直接跟IoC Service Provider打声招呼,后者就会把相应的被依赖对象注入到被注入的对象中,从而达到IoC Service Provider为被注入对象服务的目的。所以有了IoC,原来需要什么东西自己去拿,现在是需要什么让别人(IoC Service Provider)送过来。

现在看看上面那四个问题,答案就很明显了:

  1. 谁控制谁:在传统的开发模式下,我们都是采用直接new一个对象的方式来创建对象,也就是说你依赖的对象直接由你自己控制,但是有了IoC容器后,则直接由IoC容器来控制。所以“谁控制谁”,当然是IoC容器控制对象。
  2. 控制什么:控制对象
  3. 为什么是反转:没有IoC的时候我们都是在自己对象中主动区创建被依赖的对象,这就是正转。但是有了IoC后,所依赖的对象直接由IoC容器创建后注入到被注入的对象中,依赖的对象由原来的主动获取变成被动接受,所以是反转。
  4. 哪些方面反转了:所依赖对象的获取被反转了。

妹子有了,但是如何拥有妹子呢?这也是一门学问。

  1. 可能你比较厉害,刚出生就指腹为婚了
  2. 大多数情况我们还是会考虑自己想要什么样的妹子,所以还是需要向婚介公司打招呼的
  3. 还有一种情况就是,你根本不知道自己想要什么样的妹子,直接跟婚介公司说,我想要一个妹子

注入形式

所以,IoC Service Provider为被注入对象提供被依赖对象也有如下几种方式:构造方法注入、setter方法注入、接口注入

构造方法注入

顾名思义就是被注入的对象通过在其构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。

1
2
3
YoungMan(BeautifulGirl beautifulGirl) {
this.beautifulGirl = beautifulGirl;
}

构造方法注入比较直观,对象构造完毕后就可以直接使用,这就好比你出生你家里就给你制定了媳妇

setter方法注入

对于JavaBean对象而言,我们一般都是通过getter和setter方法来访问和设置对象的属性。所以,当前对象只需要为其所依赖的对象提供相对应的setter方法,就可以通过该方法将相应的依赖对象设置到被注入对象中。

1
2
3
4
5
6
7
8
public class YoungMan {

private BeautifulGirl beautifulGirl;

public void setBeautifulGirl(BeautifulGirl beautifulGirl) {
this.beautifulGirl = beautifulGirl;
}
}

相比于构造器注入,setter方式注入会显得比较宽松灵活一些,它可以在任何时候进行注入(当然是在使用依赖对象之前),这就好比你可以先把自己想要的妹子想好了,然后再跟婚介公司打招呼。

接口方式注入

接口方式注入显得比较霸道,因为它需要被依赖的对象实现不必要的接口,带有侵入性。一般不推荐这种方式。感兴趣的可以看看《依赖注入的三种实现形式 —— 接口注入(Interface Injection)》

推荐文章

关于 IoC 理论部分,笔者不在阐述,这里推荐几篇博客阅读:

各个组件

先看下图(摘自:http://singleant.iteye.com/blog/1177358)

该图为ClassPathXmlApplicationContext的类继承体系结构,虽然只有一部分,但是它基本上包含了IoC体系中大部分的核心类和接口。

下面我们就针对这个图进行简单的拆分和补充说明。

Resource 体系

org.springframework.core.io.Resource,对资源的抽象。它的每一个实现类都代表了一种资源的访问策略,如ClassPathResource、RLResource、FileSystemResource 等。

ResourceLoader 体系

有了资源,就应该有资源加载,Spring利用org.springframework.core.io.ResourceLoader来进行统一资源加载,类图如下:

(关于Resource和ResourceLoader的源码解析,见…)

BeanFactory 体系

org.springframework.beans.factory.BeanFactory,是一个非常纯粹的bean容器,它是IoC必备的数据结构,其中BeanDefinition是它的基本结构。BeanFactory内部维护着一个BeanDefinition map,并可根据BeanDefinition的描述进行bean的创建和管理。

  • BeanFactory 有三个直接子类ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory 。
  • DefaultListableBeanFactory 为最终默认实现,它实现了所有接口。

BeanDefinition 体系

org.springframework.beans.factory.config.BeanDefinition ,用来描述 Spring 中的 Bean 对象。

BeanDefinitionReader 体系

org.springframework.beans.factory.support.BeanDefinitionReader 的作用是读取 Spring 的配置文件的内容,并将其转换成 Ioc 容器内部的数据结构 :BeanDefinition 。

关于BeanDefinitionReader 的源码解析,见如下文章:

ApplicationContext 体系

org.springframework.context.ApplicationContext ,这个就是大名鼎鼎的 Spring 容器,它叫做应用上下文,与我们应用息息相关。它继承 BeanFactory ,所以它是 BeanFactory 的扩展升级版,如果BeanFactory 是屌丝的话,那么 ApplicationContext 则是名副其实的高富帅。由于 ApplicationContext 的结构就决定了它与 BeanFactory 的不同,其主要区别有:

  1. 继承 org.springframework.context.MessageSource 接口,提供国际化的标准访问策略。
  2. 继承 org.springframework.context.ApplicationEventPublisher 接口,提供强大的事件机制。
  3. 扩展 ResourceLoader ,可以用来加载多种 Resource ,可以灵活访问不同的资源。
  4. 对 Web 应用的支持。

下图来源:https://blog.csdn.net/yujin753/article/details/47043143

小结

上面的五个体系可以说是 Spring IoC 中最核心的部分,后面的博文也是针对这五个部分进行源码分析。其实IoC乍一看还是挺简单的,无非就是将配置文件(暂且认定是xml文件)进行解析,然后放到一个Map里面就差不多了。初看有道理,其实要面临的问题还是有很多的。

此系列博文为笔者学习、研究 Spring 机制和源码的学习笔记,会涉及参考别人的博文和书籍内容,如有雷同,纯属借鉴,当然 LZ 会标明参考来源。同时由于知识面和能力的问题,文章中难免会出现错误之处,如有,望各位大佬指出,不胜感激

参考

原创出处 http://cmsblogs.com/?p=2652 「小明哥」,略作修改及补充

评论