SPRINGIOC的详解_第1页
SPRINGIOC的详解_第2页
SPRINGIOC的详解_第3页
SPRINGIOC的详解_第4页
SPRINGIOC的详解_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、ioc与di 首先想说说ioc(inversion of control,控制倒转)。这是spring的核心,贯穿始终。所谓ioc,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号,想办法认识她们,投其所好送其所要,然后嘿嘿这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从jndi中查询一个),使用

2、完之后还要将对象销毁(比如connection等),对象始终会和其他的接口或类藕合起来。 那么ioc是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。spring所倡导的开发方式就是如此,所有的类

3、都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。如果你还不明白的话,我决定放弃。ioc的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过di(dependency injection,依赖注入)来实现的。比如对象a需要操作数据库,以前我

4、们总是要在a中自己编写代码来获得一个connection对象,有了 spring我们就只需要告诉spring,a中需要一个connection,至于这个connection怎么构造,何时构造,a不需要知道。在系统运行时,spring会在适当的时候制造一个connection,然后像打针一样,注射到a当中,这样就完成了对各个对象之间关系的控制。a需要依赖 connection才能正常运行,而这个connection是由spring注入到a中的,依赖注入的名字就这么来的。那么di是如何实现的呢? java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、

5、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。关于反射的相关资料请查阅java doc。 理解了ioc和di的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。如果还不明白,放弃java吧! 下面来让大家了解一下spring到底是怎么运行的。 java代码 public static void main(string args) applicationcontext context = new filesystemxmlapplicationcontext( applicationcontext.xml); animal animal = (

6、animal) context.getbean(animal); animal.say(); public static void main(string args) applicationcontext context = new filesystemxmlapplicationcontext( applicationcontext.xml); animal animal = (animal) context.getbean(animal); animal.say(); 这段代码你一定很熟悉吧,不过还是让我们分析一下它吧,首先是applicationcontext.xmljava代码 他有一

7、个类phz.springframework.test.catjava代码 public class cat implements animal private string name; public void say() system.out.println(i am + name + !); public void setname(string name) = name; public class cat implements animal private string name; public void say() system.out.println(i am + n

8、ame + !); public void setname(string name) = name; 实现了phz.springframework.test.animal接口java代码 public interface animal public void say(); public interface animal public void say();很明显上面的代码输出i am kitty! 那么到底spring是如何做到的呢? 接下来就让我们自己写个spring 来看看spring 到底是怎么运行的吧! 首先,我们定义一个bean类,这个类用来存放一个bean拥有的

9、属性java代码 /* bean id */ private string id; /* bean class */ private string type; /* bean property */ private map properties = new hashmap(); /* bean id */ private string id; /* bean class */ private string type; /* bean property */ private map properties = new hashmap();一个bean包括id,type,和properties。 接

10、下来spring 就开始加载我们的配置文件了,将我们配置的信息保存在一个hashmap中,hashmap的key就是bean 的 id ,hasmap 的value是这个bean,只有这样我们才能通过context.getbean(animal)这个方法获得animal这个类。我们都知道 spirng可以注入基本类型,而且可以注入像list,map这样的类型,接下来就让我们以map为例看看spring是怎么保存的吧 map配置可以像下面的java代码 1 2 1 2 spring是怎样保存上面的配置呢?,代码如下:java代码 if (beanproperty.element(map) !=

11、null) map propertiesmap = new hashmap(); element propertieslistmap = (element) beanproperty .elements().get(0); iterator propertiesiterator = propertieslistmap .elements().iterator(); while (propertiesiterator.hasnext() element vet = (element) propertiesiterator.next(); if (vet.getname().equals(entr

12、y) string key = vet.attributevalue(key); iterator valuesiterator = vet.elements() .iterator(); while (valuesiterator.hasnext() element value = (element) valuesiterator.next(); if (value.getname().equals(value) propertiesmap.put(key, value.gettext(); if (value.getname().equals(ref) propertiesmap.put(

13、key, new string value .attributevalue(bean) ); bean.getproperties().put(name, propertiesmap); if (beanproperty.element(map) != null) map propertiesmap = new hashmap(); element propertieslistmap = (element) beanproperty .elements().get(0); iterator propertiesiterator = propertieslistmap .elements().i

14、terator(); while (propertiesiterator.hasnext() element vet = (element) propertiesiterator.next(); if (vet.getname().equals(entry) string key = vet.attributevalue(key); iterator valuesiterator = vet.elements() .iterator(); while (valuesiterator.hasnext() element value = (element) valuesiterator.next(

15、); if (value.getname().equals(value) propertiesmap.put(key, value.gettext(); if (value.getname().equals(ref) propertiesmap.put(key, new string value .attributevalue(bean) ); bean.getproperties().put(name, propertiesmap); 接下来就进入最核心部分了,让我们看看spring 到底是怎么依赖注入的吧,其实依赖注入的思想也很简单,它是通过反射机制实现的,在实例化一个类时,它通过反射调用

16、类中set方法将事先保存在hashmap中的类属性注入到类中。让我们看看具体它是怎么做的吧。 首先实例化一个类,像这样java代码 public static object newinstance(string classname) class cls = null; object obj = null; try cls = class.forname(classname); obj = cls.newinstance(); catch (classnotfoundexception e) throw new runtimeexception(e); catch (instantiatione

17、xception e) throw new runtimeexception(e); catch (illegalaccessexception e) throw new runtimeexception(e); return obj; public static object newinstance(string classname) class cls = null; object obj = null; try cls = class.forname(classname); obj = cls.newinstance(); catch (classnotfoundexception e)

18、 throw new runtimeexception(e); catch (instantiationexception e) throw new runtimeexception(e); catch (illegalaccessexception e) throw new runtimeexception(e); return obj; 接着它将这个类的依赖注入进去,像这样java代码 public static void setproperty(object obj, string name, string value) class clazz = obj.getclass(); try

19、 string methodname = returnsetmthodname(name); method ms = clazz.getmethods(); for (method m : ms) if (m.getname().equals(methodname) if (m.getparametertypes().length = 1) class clazzparametertype = m.getparametertypes()0; setfieldvalue(clazzparametertype.getname(), value, m, obj); break; catch (sec

20、urityexception e) throw new runtimeexception(e); catch (illegalargumentexception e) throw new runtimeexception(e); catch (illegalaccessexception e) throw new runtimeexception(e); catch (invocationtargetexception e) throw new runtimeexception(e); public static void setproperty(object obj, string name

21、, string value) class clazz = obj.getclass(); try string methodname = returnsetmthodname(name); method ms = clazz.getmethods(); for (method m : ms) if (m.getname().equals(methodname) if (m.getparametertypes().length = 1) class clazzparametertype = m.getparametertypes()0; setfieldvalue(clazzparameter

22、type.getname(), value, m, obj); break; catch (securityexception e) throw new runtimeexception(e); catch (illegalargumentexception e) throw new runtimeexception(e); catch (illegalaccessexception e) throw new runtimeexception(e); catch (invocationtargetexception e) throw new runtimeexception(e); 最后它将这

23、个类的实例返回给我们,我们就可以用了。我们还是以map为例看看它是怎么做的,我写的代码里面是创建一个hashmap并把该hashmap注入到需要注入的类中,像这样,java代码 if (value instanceof map) iterator entryiterator = (map) value).entryset() .iterator(); map map = new hashmap(); while (entryiterator.hasnext() entry entrymap = (entry) entryiterator.next(); if (entrymap.getvalu

24、e() instanceof string) map.put(string) entrymap.getkey(), getbean(string) entrymap.getvalue()0); beanprocesser.setproperty(obj, property, map); if (value instanceof map) iterator entryiterator = (map) value).entryset() .iterator(); map map = new hashmap(); while (entryiterator.hasnext() entry entrym

25、ap = (entry) entryiterator.next(); if (entrymap.getvalue() instanceof string) map.put(string) entrymap.getkey(), getbean(string) entrymap.getvalue()0); beanprocesser.setproperty(obj, property, map); 好了,这样我们就可以用spring 给我们创建的类了,是不是也不是很难啊?当然spring能做到的远不止这些,这个示例程序仅仅提供了spring最核心的依赖注入功能中的一部分。1. ioc理论的背景我们

26、都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由n个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。图1:软件系统中耦合的对象如果我们打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒 针顺时针旋转,从而在表盘上产生正确的时间。图1中描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项 任务。我们可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系非常相似。对象之间的耦合关系是无法避免的,也是必要的,这

27、是协同工作的基础。现在,伴随着 工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临 更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。图2:对象之间复杂的依赖关系耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家michael mattson提出了ioc理论,用来实现对象之间的“解耦”,目前这个理论已经被成功地应用到实

28、践当中,很多的j2ee项目均采用了ioc框架产品spring。2. 什么是控制反转(ioc)ioc是inversion of control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。1996年,michael mattson在一篇有关探讨面向对象框架的文章中,首先提出了ioc 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以 后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。ioc理论提出的观点大体是这样的:借助于“第三方”实

29、现具有依 赖关系的对象之间的解耦,如下图:图3:ioc解耦过程大家看到了吧,由于引进了中间位置的“第三方”,也就是ioc容器,使得a、b、c、d这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三 方”了,全部对象的控制权全部上缴给“第三方”ioc容器,所以,ioc容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有 对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把ioc容器比喻成“粘合剂”的由来。我们再来做个试验:把上图中间的ioc容器拿掉,然后再来看看这套系统:图4:拿掉ioc容器后的系统我们现在看到的画面,就是我们要实现

30、整个系统所需要完成的全部内容。这时候,a、b、c、d这4个对象之间已经没有了耦合关系,彼此毫无联系,这样 的话,当你在实现a的时候,根本无须再去考虑b、c和d了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现ioc容器,对于系统开发而言, 这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!我们再来看看,控制反转(ioc)到底为什么要起这么个名字?我们来对比一下:软件系统在没有引入ioc容器之前,如图1所示,对象a依赖于对象b,那么对象a在初始化或者运行到某一点的时候,自己必须主动去创建对象b或者使用已经创建的对象b。无论是创建还是使用对象b,

31、控制权都在自己手上。软件系统在引入ioc容器之后,这种情形就完全改变了,如图3所示,由于ioc容器的加入,对象a与对象b之间失去了直接联系,所以,当对象a运行到需要对象b的时候,ioc容器会主动创建一个对象b注入到对象a需要的地方。通过前后的对比,我们不难看出来:对象a获得依赖对象b的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。3. ioc的别名:依赖注入(di)2004年,martin fowler探讨了同一个问题,既然ioc是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对 象的过程被反转了”。控

32、制被反转之后,获得依赖对象的过程由自身管理变为了由ioc容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖 注入(dependency injection)”。他的这个答案,实际上给出了实现ioc的方法:注入。所谓依赖注入,就是由ioc容器在运行期间,动态地将某种依赖关系注入到对 象之中。所以,依赖注入(di)和控制反转(ioc)是从不同的角度的描述的同一件事情,就是指通过引入ioc容器,利用依赖关系注入的方式,实现对象之间的解耦。我们举一个生活中的例子,来帮助理解依赖注入的过程。大家对usb接口和usb设备应该都很熟悉吧,usb为我们使用电脑提供了很大的方便,现在有很多的外部

33、设备都支持usb接口。图5:usb接口和usb设备现在,我们利用电脑主机和usb接口来实现一个任务:从外部usb设备读取一个文件。电脑主机读取文件的时候,它一点也不会关心usb接口上连接的是什么外部设备,而且它确实也无须知道。它的任务就是读取usb接口,挂接的外部设备只要符 合usb接口标准即可。所以,如果我给电脑主机连接上一个u盘,那么主机就从u盘上读取文件;如果我给电脑主机连接上一个外置硬盘,那么电脑主机就从外置 硬盘上读取文件。挂接外部设备的权力由我作主,即控制权归我,至于usb接口挂接的是什么设备,电脑主机是决定不了,它只能被动的接受。电脑主机需要外部 设备的时候,根本不用它告诉我,我

34、就会主动帮它挂上它想要的外部设备,你看我的服务是多么的到位。这就是我们生活中常见的一个依赖注入的例子。在这个过程 中,我就起到了ioc容器的作用。通过这个例子,依赖注入的思路已经非常清楚:当电脑主机读取文件的时候,我就把它所要依赖的外部设备,帮他挂接上。整个外部设备注入的过程和一个被依赖的对象在系统运行时被注入另外一个对象内部的过程完全一样。我们把依赖注入应用到软件系统中,再来描述一下这个过程:对象a依赖于对象b,当对象 a需要用到对象b的时候,ioc容器就会立即创建一个对象b送给对象a。ioc容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了, 而再也不用去关心你所用的东西

35、是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由ioc容器包办。在传统的实现中,由程序内部代码来控制组件之间的关系。我们经常使用new关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。ioc 很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。4. ioc为我们带来了什么好处我们还是从usb的例子说起,使用usb外部设备比使用内置硬盘,到底带来什么好处?第一、usb设备作为电脑主机的外部设备,在插入主机之前,与电脑主机没有任何的关系,只有被我们连接在一起之后,两者才发生联系,具有相关性。所以,无 论

36、两者中的任何一方出现什么的问题,都不会影响另一方的运行。这种特性体现在软件工程中,就是可维护性比较好,非常便于进行单元测试,便于调试程序和诊断 故障。代码中的每一个class都可以单独测试,彼此之间互不影响,只要保证自身的功能无误即可,这就是组件之间低耦合或者无耦合带来的好处。第二、usb设备和电脑主机的之间无关性,还带来了另外一个好处,生产usb设备的厂商和生产电脑主机的厂商完全可以是互不相干的人,各干各事,他们之间 唯一需要遵守的就是usb接口标准。这种特性体现在软件开发过程中,好处可是太大了。每个开发团队的成员都只需要关心实现自身的业务逻辑,完全不用去关心 其它的人工作进展,因为你的任务

37、跟别人没有任何关系,你的任务可以单独测试,你的任务也不用依赖于别人的组件,再也不用扯不清责任了。所以,在一个大中型 项目中,团队成员分工明确、责任明晰,很容易将一个大的任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高。第三、同一个usb外部设备可以插接到任何支持usb的设备,可以插接到电脑主机,也可以插接到dv机,usb外部设备可以被反复利用。在软件工程中,这 种特性就是可复用性好,我们可以把具有普遍性的常用组件独立出来,反复利用到项目中的其它部分,或者是其它项目,当然这也是面向对象的基本特征。显 然,ioc不仅更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准的实现,都可以

38、插接到支持此标准的模块中。第四、同usb外部设备一样,模块具有热插拔特性。ioc生成对象的方式转为外置方式,也就是把对象生成放在配置文件里进行定义,这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完全具有热插拨的特性。以上几点好处,难道还不足以打动我们,让我们在项目开发过程中使用ioc框架吗?5. ioc容器的技术剖析ioc中最基本的技术就是“反射(reflection)”编程,目前.net c#、java和php5等语言均支持,其中php5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和用法,大家应该都很清楚,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。反射的应用是很广泛的,很多的成熟的框架,比如象java中的h

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论