----------------siwuxie095
浅谈 IoC
IoC(Inversion of Control,控制反转)是 Spring 的核心,贯穿始终
所谓 IoC,对于 Spring 框架来说,就是由 Spring 来负责控制对象的
生命周期和对象间的关系:
- 传统开发模式:对象之间互相依赖
- IoC 开发模式:IoC 容器安排对象之间的依赖
传统开发模式 和 IoC 开发模式 的生动实例:
(1)在传统开发模式下,各个对象自己来维护各自的依赖关系
这是什么意思呢,其实就类似于找女朋友,在传统模式下,需要到处去看:
哪里有长得漂亮、身材又好的女孩子,然后打听她的兴趣爱好、QQ号、电
话 … 想办法认识她,投其所好,送其所要 …
这个过程是复杂深奥的,必须自己设计和面对每个环节
传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须
得到它(自己 new 一个,或 从 JNDI 中查询一个),使用完之后还要将对象
销毁(如:数据库的连接 Connection 等),对象始终会和其他的接口 或 类
藕合起来
(2)在 IoC 开发模式下,对象之间的依赖关系变成了由 IoC 容器来进行管理
那么 IoC 是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了
一个第三者:婚姻介绍所
婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个
什么样的女朋友,然后婚介就会按照我的要求,提供一个女孩子,我只需要去和
她谈恋爱、结婚就行了,简单明了
如果婚介介绍的人选不符合要求,我就会抛出异常。整个过程不再由我自己控制,
而是由婚介这样一个类似容器的机构来控制
Spring 所倡导的开发方式就是如此,所有的类都会在 Spring 容器中登记,告诉
Spring 你是个什么东西,你需要什么东西,然后 Spring 会在系统运行到适当的
时候,把你要的东西主动给你,同时也把你交给其他需要你的东西
所有类的创建、销毁都由 Spring 来控制,即 控制对象生存周期的不再是引用它
的对象,而是 Spring
对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被 Spring
所控制,所以叫 控制反转
IoC 的理论背景
从理论的角度来看 IoC 的由来和它的存在价值:
图 1 表示传统系统中对象之间相互引用
在采用面向对象方法设计的软件系统中,其底层实现都是由 N 个对象组成的,
所有对象通过彼此间合作,最终实现系统的业务逻辑
这类似于机械手表,如果打开机械手表的后盖,就会看到:各个齿轮分别带动
时针、分针 和 秒针 顺时针旋转,从而在表盘上产生正确的时间
图 1 所描述的就是这样一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互耦合
在一起,共同完成某项任务
在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常
运转,齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系非常相似
对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础
现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,
经常会出现对象之间的多重依赖关系,因此,架构师和设计师对于系统的分析和
设计,将面临更大的挑战
对象之间的耦合度过高的系统,必然会出现牵一发而动全身的情形
耦合关系不仅会出现在 对象与对象之间,也会出现在 软件系统的各模块之间,
以及 软件系统和硬件系统之间。降低系统之间、模块之间 和 对象之间 的耦合
度是 软件工程 永远追求的目标之一
为了解决对象之间耦合度过高的问题,软件专家 Michael Mattson 提出
了 IoC 理论,用来实现对象之间的 "解耦"
目前这个理论已经被成功地应用到实践当中,很多的 J2EE 项目都采用了
IoC 框架产品 Spring
图 2 表示利用 IoC 原理为对象之间的关系进行解耦
IoC 的处理方式,简单来说,就是把复杂系统分解成相互合作的对象
这些对象类通过封装以后,内部实现对于外部是透明的,从而降低了
解决问题的复杂度,而且可以灵活的被重用和扩展
IoC 理论观点:借助于 "第三方" 实现具有依赖关系的对象之间的解耦
在图 2 中,由于引进了中间的 "第三方",即 IoC 容器,使得 A、B、C、D
这 4 个对象没有了耦合关系,齿轮之间的传动全部依靠 "第三方",全部对象
的控制权全部上缴给 "第三方" IoC 容器
所以,IoC 容器成了整个系统的关键核心,它起到了一种类似 "粘合剂" 的作用,
把系统中的所有对象粘合在一起发挥作用
如果没有这个 "粘合剂",对象与对象之间会彼此失去联系,这就是有人把 IoC
容器比喻成 "粘合剂" 的由来
图 3 表示完全解耦的理想系统
拿掉中间的 IoC 容器,就是要实现整个系统所需要完成的全部内容
这时,A、B、C、D 这 4 个对象之间已经没有了耦合关系,彼此毫无联系
这样的话,当在实现 A 时,根本无须再去考虑 B、C 和 D,对象之间的依赖
关系已经降低到了最低程度
所以,如果真能实现 IoC 容器,对于系统开发而言,将是一件美好的事情,
参与开发的成员只要实现自己的类即可,跟别人没有任何关系!
控制反转(IoC) 名字的由来:
软件系统在没有引入 IoC 容器之前,如图 1 所示,对象 A 依赖于对象 B,
那么对象 A 在初始化 或运行到某一点时,自己必须主动去创建对象 B 或
使用已创建的对象 B。无论是创建还是使用对象 B,控制权都在自己手上
软件系统在引入 IoC 容器之后,这种情形就完全改变了,如图 2 所示,由于
IoC 容器的加入,对象 A 与对象 B 之间失去了直接联系,所以,当对象 A
运行到需要对象 B 时,IoC 容器会主动创建一个对象 B 注入到对象 A 需要的
地方
通过前后对比,不难看出:对象 A 获得依赖对象 B 的过程,由主动行为变成了
被动行为,控制权颠倒了过来,这就是 "控制反转" 的由来
依赖注入(DI)
2004 年,Martin Fowler 探讨了同一问题,既然 IoC 是控制反转,
那么到底是"哪些方面的控制被反转了呢?",经过详细地分析和
论证后,他得出了答案:"获得依赖对象的过程被反转了"
控制被反转之后,获得依赖对象的过程由自身管理变为了由 IoC
容器主动注入。于是,他给"控制反转"取了一个更合适的名字
叫做"依赖注入(Dependency Injection)"
所谓 依赖注入,就是由 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中
所以,依赖注入(DI)和 控制反转(IoC)是从不同角度描述的同一件事情,
即 通过引入 IoC 容器,利用依赖关系注入的方式,实现对象之间的解耦
以 USB 接口和 USB 设备为例理解 依赖注入 的过程:
USB 为电脑的使用提供了很大的方便,现在很多外部设备都支持 USB 接口
现在,利用电脑主机和 USB 接口来实现一个任务:从外部 USB 设备读取一个文件
电脑主机读取文件时,它一点也不关心 USB 接口上连接的是什么外部设备,
而且它也没必要知道。它的任务就是读取 USB 接口,挂接的外部设备只要符
合 USB 接口标准即可
所以,如果 "我" 给电脑主机连接一个 U 盘,那么主机就从 U 盘上读取
文件;如果 "我" 给电脑主机连接一个外置硬盘,那么电脑主机就从外置
硬盘上读取文件
挂接外部设备的权力由 "我" 作主,即 控制权归 "我",至于 USB 接口挂接的
是什么设备,电脑主机决定不了,它只能被动的接受
电脑主机需要外部设备时,根本不用它告诉 "我","我" 就会主动帮它挂上
它想要的外部设备
这就是一个依赖注入的例子,整个过程中,"我" 就起到了 IoC 容器的作用
通过这个例子,依赖注入的思路已经非常清楚:
当电脑主机读取文件时,"我" 就把它所要依赖的外部设备,帮它挂接上
整个外部设备注入的过程和一个被依赖的对象在系统运行时被注入另外一个对象
内部的过程完全一样
把 依赖注入 应用到软件系统中,再来描述这个过程:
对象 A 依赖于对象 B,当对象 A 需要用到对象 B 时,IoC 容器就会立即创建
一个对象 B 送给对象 A。IoC 容器就是一个对象制造工厂,你需要什么,它就
会传送给你,你直接使用即可,而再也不用关心你所用的东西是如何制成的,
也不用关心最后是怎么被销毁的,这一切全部由 IoC 容器包办
在传统的实现中,由程序内部代码来控制组件之间的关系。我们经常使用
new 关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之
间的耦合
IoC 很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,
即 由容器在运行期将组件间的某种依赖关系动态注入组件中
IoC 的好处
IoC 在编程过程中不会对业务对象构成很强的侵入性,使用 IoC 之后,
对象具有更好的可实行性、可重用性和可扩展性:
- 降低组件之间的耦合度
- 提高开发效率和产品质量
- 统一标准,提高模块的复用性
- 模块具有热插拔特性
仍以 USB 为例:
使用 USB 外部设备比使用内置硬盘,到底带来什么好处?
(1)USB 设备作为电脑主机的外部设备,在插入主机之前,与电脑主机
没有任何关系,只有被连接在一起之后,两者才发生联系,具有相关性,
所以,无论两者中的任何一方出现什么的问题,都不会影响另一方的运行
这种特性体现在软件工程中,就是所谓的可维护性,非常便于进行单元测试,
便于调试程序和诊断故障。代码中的每一个 Class 都可以单独测试,彼此间
互不影响,只要保证自身功能无误即可,这就是组件之间低耦合 或无耦合
带来的好处
(2)USB 设备和电脑主机的之间无关性,还带来了另外一个好处,生产 USB
设备的厂商和生产电脑主机的厂商完全可以是互不相干的人,各干各事,他们
之间唯一需要遵守的就是USB接口标准
这种特性体现在软件开发过程中,好处不可谓不大。每个开发团队的成员都只需
关心实现自身的业务逻辑,完全不用去关心其它的人工作进展,因为你的任务跟
别人没有任何关系,你的任务可以单独测试,你的任务也不用依赖于别人的组件,
再也不用扯不清责任。
所以,在一个大中型项目中,团队成员分工明确、责任明晰,很容易将一个大的
任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高
(3)同一个 USB 外部设备可以插接到任何支持 USB 的设备,可以插接到电脑
主机,也可以插接到 DV 机,USB 外部设备可以被反复利用
在软件工程中,这种特性就是可复用性好。我们可以把具有普遍性的常用组件
独立出来,反复利用到项目中的其它部分,或是其它项目,当然,这也是面向
对象的基本特征
显然,IoC 不仅更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准
的实现,都可以插接到支持此标准的模块中
(4)同 USB 外部设备一样,模块具有热插拔特性。IoC 生成对象的方式转为
外置方式,即 把对象生成放在配置文件里进行定义
这样,更换一个实现子类将会变得很简单,只要修改配置文件即可,完全具有
热插拨的特性
IoC 的通俗理解
- IoC 控制反转
创建对象实例的控制权从代码控制剥离到 IoC 容器控制,实际就是
在 XML 文件中控制,侧重于原理
- DI 依赖注入
创建对象实例时,为这个对象注入属性值 或其它对象实例,侧重于实现
【made by siwuxie095】