Spring的IOC原理(通俗易懂)
1. 理论的背景
我们都知道,在采⽤⽅法设计的软件系统中,它的底层实现都是由Ñ个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。
如果我们打开机械式⼿表的后盖,就会看到与上⾯类似的情形,各个齿轮分别带动时针,分针和秒针顺时针旋转,从⽽在表盘上产⽣正确的时间。图1中描述的就是这样的⼀个齿轮组,它拥有多个独⽴的齿轮,这些齿轮相互啮合在⼀起,协同⼯作,共同完成某项任务。我们可以看到,在这样的齿轮组中,如果有⼀个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。
齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系⾮常相似。对象之间的耦合关系是⽆法避免的,也是必要的,这是协同⼯作的基础。现在,伴随着⼯业级应⽤的规模越来越庞⼤,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将⾯临更⼤的挑战。对象之间耦合度过⾼的系统,必然会出现牵⼀发⽽动全⾝的情形。
耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间,模块之间和对象之间的耦合度,是软件⼯程永远追求的⽬标之⼀。为了解决对象之间的耦合度过⾼的问题,软件专家Michael Mattson提出了IOC理论,⽤来实现对象之间的“
解耦”,⽬前这个理论已经被成功地应⽤到实践当中,很多的J2EE项⽬均采⽤了国际奥委会产品Spring。
2.什么是控制反转(IoC) IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。 1996年,Michael Mattson在⼀篇有关探讨⾯向对象框架的⽂章中,⾸先提出了IOC这个概念。对于⾯向对象设计及编程的基本思想,前⾯我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互作⽤合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从⽽降低了解决问题的复杂度,⽽且可以灵活地被重⽤和扩展.IOC理论提出的观点⼤体是这样的:借助
于“第三⽅”实现具有依赖关系的对象之间的,如下图:
spring到底是干啥的⼤家看到了吧,由于引进了中间位置的“第三⽅”,也就是IOC容器,使得A,B,C,d这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三⽅”了,全部对象的控制权全部上缴给“第三⽅” IOC容器,所以,IOC容器成了整个系统的关键核⼼,它起到了⼀种类
似“粘合剂”的作⽤,把系统中的所有对象粘合在⼀起发挥作⽤,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有⼈把IOC容器⽐喻成“粘合剂”的由来。
我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:
我们现在看到的画⾯,就是我们要实现整个系统所需要完成的全部内容。这时候,A,B,C,d这4个对象之间已经没有了耦合关系,彼此毫⽆联系,这样的话,当你在实现阿的时候,根本⽆须再去考虑B,
C和d了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发⽽⾔,这将是⼀件多么美好的事情,参与开发的每⼀成员只要实现⾃⼰的类就可以了,跟别⼈没有任何关系!
我们再来看看,控制反转(IOC)到底为什么要起这么个名字我们来对⽐⼀下?
软件系统在没有引⼊IOC容器之前,如图1所⽰,对象甲依赖于对象B,那么对象阿在初始化或者运⾏到某⼀点的时候,⾃⼰必须主动去创建对象⼄或者使⽤已经创建的对象B.⽆论是创建还是使⽤对象B,控制权都在⾃⼰⼿上。
软件系统在引⼊IOC容器之后,这种情形就完全改变了,如图3所⽰,由于IOC容器的加⼊,对象甲与对象⼄之间失去了直接联系,所以,当对象甲运⾏到需要对象⼄的时候,IOC容器会主动创建⼀个对象⼄注⼊到对象甲需要的地⽅。
通过前后的对⽐,我们不难看出来:对象甲获得依赖对象⼄的过程,由主动⾏为变为了被动⾏为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
3. IOC的别名:依赖注⼊(DI) 2004年,Martin Fowler探讨了同⼀个问题,既然IOC是控制反转,那么到底是“哪些⽅⾯的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依
赖对象的过程被反转了”控制被反转之后,获得依赖对象的过程由⾃⾝管理IOC容器主动注⼊于是,他给“控制反转“取了⼀个更合适的名字叫做”依赖注⼊(Dependency Injection)“。他的这个答案,实际上给出了实现IOC的⽅法:注⼊。所谓依赖注⼊,就是由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很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运⾏期将组件间的某种依赖关系动态注⼊组件中。
4. IOC为我们带来了什么好处
我们还是从USB的例⼦说起,使⽤USB外部设备⽐使⽤内置硬盘,到底带来什么好处?
第⼀、USB设备作为电脑主机的外部设备,在插⼊主机之前,与电脑主机没有任何的关系,只有被我们连接在⼀起之后,两者才发⽣联系,具有相关性。所以,⽆论两者中的任何⼀⽅出现什么的问题,都不会影响另⼀⽅的运⾏。这种特性体现在软件⼯程中,就是可维护性⽐较好,⾮常便于进⾏单元测试,便于调试程序和诊断故障。代码中的每⼀个Class都可以单独测试,彼此之间互不影响,只要保证⾃⾝的功能⽆误即可,这就是组件之间低耦合或者⽆耦合带来的好处。
第⼆、USB设备和电脑主机的之间⽆关性,还带来了另外⼀个好处,⽣产USB设备的⼚商和⽣产电脑主机的⼚商完全可以是互不相⼲的⼈,各⼲各事,他们之间唯⼀需要遵守的就是USB接⼝标准。这种特性体现在软件开发过程中,好处可是太⼤了。每个开发团队的成员都只需要关⼼实现⾃⾝的业务逻辑,完全不⽤去关⼼其它的⼈⼯作进展,因为你的任务跟别⼈没有任何关系,你的任务可以单独测试,你的任务也不⽤依赖于别⼈的组件,再也不⽤扯不清责任了。所以,在⼀个⼤中型项⽬中,团队成员分⼯明确、责任明晰,很容易将⼀个⼤的任务划分为细⼩的任务,开发效率和产品质量必将得到
⼤幅度的提⾼。
第三、同⼀个USB外部设备可以插接到任何⽀持USB的设备,可以插接到电脑主机,也可以插接到DV机,USB外部设备可以被反复利⽤。在软件⼯程中,这种特性就是可复⽤性好,我们可以把具有普遍性的常⽤组件独⽴出来,反复利⽤到项⽬中的其它部分,或者是其它项⽬,当然这也是⾯向对象的基本特征。显然,IOC不仅更好地贯彻了这个原则,提⾼了模块的可复⽤性。符合接⼝标准的实现,都可以插接到⽀持此标准的模块中。
第四、同USB外部设备⼀样,模块具有热插拔特性。IOC⽣成对象的⽅式转为外置⽅式,也就是把对象⽣成放在配置⽂件⾥进⾏定义,这样,当我们更换⼀个实现⼦类将会变得很简单,只要修改配置⽂件就可以了,完全具有热插拨的特性。
以上⼏点好处,难道还不⾜以打动我们,让我们在项⽬开发过程中使⽤IOC框架吗?
5. IOC容器的技术剖析
IOC中最基本的技术就是“反射(Reflection)”编程,⽬前.Net C#、Java和PHP5等语⾔均⽀持,其中PHP5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和⽤法,⼤家应该都很清楚,通俗来讲就是根据给出的类名(字符串⽅式)来动态地⽣成对象。这种编程⽅式可以让对象在⽣成时才决定
到底是哪⼀种对象。反射的应⽤是很⼴泛的,很多的成熟的框架,⽐如象Java中的Hibernate、框架,.Net中 NHibernate、Spring.Net框架都是把“反射”做为最基本的技术⼿段。
反射技术其实很早就出现了,但⼀直被忽略,没有被进⼀步的利⽤。当时的反射编程⽅式相对于正常的对象⽣成⽅式要慢⾄少得10倍。现在的反射技术经过改良优化,已经⾮常成熟,反射⽅式⽣成对象和通常对象⽣成⽅式,速度已经相差不⼤了,⼤约为1-2倍的差距。
我们可以把IOC容器的⼯作模式看做是⼯⼚模式的升华,可以把IOC容器看作是⼀个⼯⼚,这个⼯⼚⾥要⽣产的对象都在配置⽂件中给出定义,然后利⽤编程语⾔的的反射编程,根据配置⽂件中给出的类名⽣成相应的对象。从实现来看,IOC是把以前在⼯⼚⽅法⾥写死的对象⽣成代码,改变为由配置⽂件来定义,也就是把⼯⼚和对象⽣成这两者独⽴分隔开来,⽬的就是提⾼灵活性和可维护性。
6. IOC容器的⼀些产品
Sun ONE技术体系下的IOC容器有:轻量级的有Spring、Guice、Pico Container、Avalon、HiveMind;重量级的有EJB;不轻不重的有JBoss,Jdon等等。Spring框架作为Java开发中SSH(Struts、Spring、Hibernate)三剑客之⼀,⼤中⼩项⽬中都有使⽤,⾮常成熟,应⽤⼴泛,EJB在关键性的⼯业级项⽬中也被使⽤,⽐如某些电信业务。
.
Net技术体系下的IOC容器有:Spring.Net、Castle等等。Spring.Net是从Java的Spring移植过来的IOC容器,Castle的IOC容器就是Windsor部分。它们均是轻量级的框架,⽐较成熟,其中Spring.Net已经被逐渐应⽤于各种项⽬中。
7. 使⽤IOC框架应该注意什么
使⽤IOC框架产品能够给我们的开发过程带来很⼤的好处,但是也要充分认识引⼊IOC框架的缺点,做到⼼中有数,杜绝滥⽤框架。
第⼀、软件系统中由于引⼊了第三⽅IOC容器,⽣成对象的步骤变得有些复杂,本来是两者之间的事情,⼜凭空多出⼀道⼿续,所以,我们在刚开始使⽤IOC框架的时候,会感觉系统变得不太直观。所以,引⼊了⼀个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运⾏维护中,还得让新加⼊者具备同样的知识体系。
第⼆、由于IOC容器⽣成对象是通过反射⽅式,在运⾏效率上有⼀定的损耗。如果你要追求运⾏效率的话,就必须对此进⾏权衡。
第三、具体到IOC框架产品(⽐如:Spring)来讲,需要进⾏⼤量的配制⼯作,⽐较繁琐,对于⼀些⼩的项⽬⽽⾔,客观上也可能加⼤⼀些⼯作成本。
第四、IOC框架产品本⾝的成熟度需要进⾏评估,如果引⼊⼀个不成熟的IOC框架产品,那么会影响到整个项⽬,所以这也是⼀个隐性的风险。
我们⼤体可以得出这样的结论:⼀些⼯作量不⼤的项⽬或者产品,不太适合使⽤IOC框架产品。另外,如果团队成员的知识能⼒⽋缺,对于IOC框架产品缺乏深⼊的理解,也不要贸然引⼊。最后,特别强调运⾏效率的项⽬或者产品,也不太适合引⼊IOC框架产品,象WEB2.0⽹站就是这种情况。
开始编写简单易懂的java思维导图啦,有兴趣的,可以关注我。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论