深入理解 IoC
深入理解 IoC
1. IoC 理论
IoC 全称为 Inversion of Control,翻译为 “控制反转”,它还有一个别名为 DI(Dependency Injection),即依赖注入。
如何理解 “控制反转” 呢?关键在于我们需要回答如下四个问题:
- 谁控制谁
- 控制什么
- 为何是反转
- 哪些方面反转了
在回答这四个问题之前,我们先看 IoC 的定义:
IoC
所谓 IoC ,就是由 Spring IoC 容器来负责对象的生命周期和对象之间的关系。
在没有引入 IoC 的时候,被注入的对象直接依赖于被依赖的对象,有了 IoC 后,两者及其他们的关系都是通过 Ioc Service Provider 来统一管理维护的。被注入的对象需要什么,直接跟 IoC Service Provider 打声招呼,后者就会把相应的被依赖对象注入到被注入的对象中,从而达到 IoC Service Provider 为被注入对象服务的目的。所以 IoC 就是这么简单!原来是需要什么东西自己去拿,现在是需要什么东西让别人(IoC Service Provider)送过来。
流程如下:
现在在看上面那四个问题,答案就显得非常明显了:
- 谁控制谁:在传统的开发模式下,我们都是采用直接 new 一个对象的方式来创建对象,也就是说你依赖的对象直接由你自己控制,但是有了 IoC 容器后,则直接由 IoC 容器来控制。所以“谁控制谁”,当然是 IoC 容器控制对象
- 控制什么:控制对象。
- 为何是反转:没有 IoC 的时候我们都是在自己对象中主动去创建被依赖的对象,这是正转。但是有了 IoC 后,所依赖的对象直接由 IoC 容器创建后注入到被注入的对象中,依赖的对象由原来的主动获取变成被动接受,所以是反转。
- 哪些方面反转了:所依赖对象的获取被反转了。
2. Bromon 的 blog 上对 IoC 与 DI 浅显易懂的讲解
2.1 IoC
首先想说说 IoC(Inversion of Control,控制反转)。这是 spring 的核心,贯穿始终。
所谓 IoC,对于 spring 框架来说,就是由 spring 来负责控制对象的生命周期和对象间的关系。
这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己 new 一个,或者从 JNDI 中查询一个),使用完之后还要将对象销毁(比如 Connection 等),对象始终会和其他的接口或类藕合起来。
那么 IoC 是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在 spring 容器中登记,告诉 spring 你是个什么东西,你需要什么东西,然后 spring 会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。
所有的类的创建、销毁都由 spring 来控制,也就是说控制对象生存周期的不再是引用它的对象,而是 spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 spring 控制,所以这叫控制反转。
即:获得依赖对象的方式反转了。
2.2 DI
IoC 的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过 DI(Dependency Injection,依赖注入)来实现的。
比如对象 A 需要操作数据库,以前我们总是要在 A 中自己编写代码来获得一个 Connection 对象,有了 spring 我们就只需要告诉 spring,A 中需要一个 Connection,至于这个 Connection 怎么构造,何时构造,A 不需要知道。在系统运行时,spring 会在适当的时候制造一个 Connection,然后像打针一样,注射到 A 当中,这样就完成了对各个对象之间关系的控制。A 需要依赖 Connection 才能正常运行,而这个 Connection 是由 spring 注入到 A 中的,依赖注入的名字就这么来的。
那么 DI 是如何实现的呢?Java 1.3 之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring 就是通过反射来实现注入的。
理解了 IoC 和 DI 的概念后,一切都将变得简单明了,剩下的工作只是在 spring 的框架中堆积木而已。
3. 注入形式
IoC Service Provider 为被注入对象提供被依赖对象有如下几种方式:构造方法注入、stter方法注入、接口注入。
1)构造器注入
构造器注入,顾名思义就是被注入的对象通过在其构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。
YoungMan(BeautifulGirl beautifulGirl) {
this.beautifulGirl = beautifulGirl;
}
构造器注入方式比较直观,对象构造完毕后就可以直接使用,这就好比你出生你家里就给你指定了你媳妇。
2)setter
方法注入
对于 JavaBean 对象而言,我们一般都是通过 getter
和 setter
方法来访问和设置对象的属性。所以,当前对象只需要为其所依赖的对象提供相对应的 setter
方法,就可以通过该方法将相应的依赖对象设置到被注入对象中。如下:
public class YoungMan {
private BeautifulGirl beautifulGirl;
public void setBeautifulGirl(BeautifulGirl beautifulGirl) {
this.beautifulGirl = beautifulGirl;
}
}
相比于构造器注入,setter
方式注入会显得比较宽松灵活些,它可以在任何时候进行注入(当然是在使用依赖对象之前),这就好比你可以先把自己想要的妹子想好了,然后再跟婚介公司打招呼,你可以要林志玲款式的,赵丽颖款式的,甚至凤姐哪款的,随意性较强。
3)接口方式注入
接口方式注入显得比较霸道,因为它需要被依赖的对象实现不必要的接口,带有侵入性。一般都不推荐这种方式。
可以看下 《依赖注入的三种实现形式 —— 接口注入(Interface Injection)》
4. 各个组件
先上图:
该图为 ClassPathXmlApplicationContext 的类继承体系结构,虽然只有一部分,但是它基本上包含了 IoC 体系中大部分的核心类和接口。
下面我们就针对这个图进行简单的拆分和补充说明
4.1 Resource 体系
org.springframework.core.io.Resource
,对资源的抽象。它的每一个实现类都代表了一种资源的访问策略,如 ClassPathResource、RLResource、FileSystemResource 等。
4.2 ResourceLoader 体系
有了资源,就应该有资源加载,Spring 利用 org.springframework.core.io.ResourceLoader
来进行统一资源加载,类图如下:
4.3 BeanFactory 体系
org.springframework.beans.factory.BeanFactory
,是一个非常纯粹的 bean 容器,它是 IoC 必备的数据结构,其中 BeanDefinition 是它的基本结构。BeanFactory 内部维护着一个BeanDefinition map ,并可根据 BeanDefinition 的描述进行 bean 的创建和管理。
- BeanFactory 有三个直接子类 ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory 。
- DefaultListableBeanFactory 为最终默认实现,它实现了所有接口。
4.4 BeanDefinition 体系
org.springframework.beans.factory.config.BeanDefinition
,用来描述 Spring 中的 Bean 对象。
4.5 BeanDefinitionReader 体系
org.springframework.beans.factory.support.BeanDefinitionReader
的作用是读取 Spring 的配置文件的内容,并将其转换成 Ioc 容器内部的数据结构:BeanDefinition 。
4.6 ApplicationContext 体系
org.springframework.context.ApplicationContext
,这个就是大名鼎鼎的 Spring 容器,它叫做应用上下文,与我们应用息息相关。它继承 BeanFactory ,所以它是 BeanFactory 的扩展升级版,如果BeanFactory 是屌丝的话,那么 ApplicationContext 则是名副其实的高富帅。由于 ApplicationContext 的结构就决定了它与 BeanFactory 的不同,其主要区别有:
- 继承
org.springframework.context.MessageSource
接口,提供国际化的标准访问策略。 - 继承
org.springframework.context.ApplicationEventPublisher
接口,提供强大的事件机制。 - 扩展
ResourceLoader
,可以用来加载多种 Resource ,可以灵活访问不同的资源。 - 对 Web 应用的支持。
下图来源:https://blog.csdn.net/yujin753/article/details/47043143
4.7 小结
上面五个体系可以说是 Spring IoC 中最核心的部分,后续也会针对这五个部分进行分析。其实 IoC 咋一看还是挺简单的,无非就是将配置文件(暂且认为是 xml 文件)进行解析(分析 xml 谁不会啊),然后放到一个 Map 里面就差不多了,初看有道理,其实要面临的问题还是有很多的。
另外,通过上面五个体系,我们可以看出,IoC 主要由 spring-beans
和 spring-context
项目,进行实现。