浏览 2186 次
该帖已经被评为隐藏帖
|
|||
---|---|---|---|
作者 | 正文 | ||
发表时间:2008-03-31
注:希望大家看后,请给我一点评价,无论写的怎么样,希望你们能给我支持。提出你宝贵的意见。我会继续完善。谢谢您。朋友。 以下这部分是第二章后面的. (2)IOC 是一种使应用程序逻辑外在化的设计模式 因为提供服务的组件是被注入而不是被写入到客户机代码中。将 IOC 与接口编程应用结合从而产生出 Spring 框架的架构,这种架构能够减少客户机对特定实现逻辑的依赖。 (3)IoC的设计目标 不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器(在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。 (4)IoC在应用开发中的体现 IoC的抽象概念是“依赖关系的转移”,在实际应用中的下面的各个规则其实都是IoC在应用开发中的体现。 l “高层模块组件不应该依赖低层模块组件,而是模块组件都必须依赖于抽象”是 IoC的一种表现 l “实现必须依赖抽象,而不是抽象依赖实现”也是IoC的一种表现 l “应用程序不应依赖于容器,而是容器服务于应用程序”也是IoC的一种表现。 接下来我们讲在讲述它的另外的一个名字:依赖注入(DI)。 Spring 中的依赖注入(DI) (1)DI = Dependency Injection 正在业界为IoC争吵不休时《Inversion of Control Containers and the Dependency Injection pattern》为IoC正名,至此,IoC又获得了一个新的名字:“依赖注入(Dependency Injection)”。 Dependency Injection模式是依赖注射的意思,也就是将依赖先剥离,然后在适当时候再注射进入。 (2)何谓依赖注入 相对IoC 而言,“依赖注入”的确更加准确地描述了这种古老而又时兴的设计理念。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。 讲的通俗点,就是在运行期,由Spring根据配置文件,将其他对象的引用通过组件的提供的setter方法或者构造方法等进行设定。 在上面的UserService中已经体现了这种方式,当UserService需要的UserLogin的时候,容器会给它注入。这就体现了需要用的时候,有容器给你注入。你不必主动的如创建了。也不用如管理它了。是不是和以前的编程方式有些改变。可能你会想到,这不就是工厂模式的衍生吗?不错它就是利用工厂模式的原理实现的,但它原比工厂模式简单。通过上面的部分代码我们就可以看出它就是工厂模式的衍生,BeanFactory就充分说明了这一点。 我接下来通过讲一个比较接近生活中的例子来说名依赖注入的原理。 图解“依赖注入”(摘录网上资料) (1)IT人员的标准“行头” 上面是我们常用的工作装备,笔记本电脑一台、USB硬盘和U盘各一只。想必大家在日常工作中也有类似的一套行头。这与依赖注入有什么关系? (2)图解“依赖注入”---在运行时由容器将依赖关系注入到组件中 图中三个设备都有一个共同点,都支持USB 接口。当我们需要将数据复制到外围存储设备时,可以 根据情况,选择是保存在U盘还是USB硬盘,下面的操作大家也都轻车熟路,无非接通USB接口,然后在资源浏览器中将选定的文件拖放到指定的盘符。 这样的操作在过去几年中每天都在我们身边发生,而这也正是所谓依赖注入的一个典型案例,再看上例中,笔记本电脑与外围存储设备通过预先指定的一个接口(USB)相连,对于笔记本而言,只是将用户指定的数据发送到USB接口,而这些数据何去何从,则由当前接入的USB设备决定。 在USB设备加载之前,笔记本不可能预料用户将在USB接口上接入何种设备,只有USB设备接入之后,这种设备之间的依赖关系才开始形成。 对应上面关于依赖注入机制的描述,在运行时(系统开机,USB 设备加载)由容器(运行在笔记本中的Windows操作系统)将依赖关系(笔记本依赖USB设备进行数据存取)注入到组件中(Windows文件访问组件)。这就是依赖注入模式在现实世界中的一个版本。 在Spring中为什么要提供“依赖注入”设计理念 (1)目的 依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。 (2)原因---更简洁的编程实现 很多初学者常常陷入"依赖注入,何用之有?"的疑惑。想来前面和下面的例子可以帮助大家简单的理解其中的含义。 回顾上面创建Spring_chap2的例子中,UserService类在运行前,其userName,passWord节点为空。运行后由容器将字符串"admin"和"1234"注入。此时UserService即与内存中的"admin"和"1234"字符串对象建立了依赖关系。也许区区一个字符串我们无法感受出依赖关系的存在。 如果把这里的userName/passWord属性换成一个数据源(DataSource),可能更有感觉: <beans> <bean id="dataSource" class="org.springframework.indi.JndiObjectFactoryBean"> <property name="jndiName">
</property> </bean> <bean id="dataBean" class="examples.DAOBean"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> </beans> 其中DAOBean(假设DAOBean是一个运行在J2EE容器中的组件---如Weblogic或者Tomcat等)中的dataSource将由容器在运行期动态注入,而DataSource的具体配置和初始化工作也将由容器在运行期完成。 对比传统的实现方式(如通过编码初始化DataSource实例),我们可以看到,基于依赖注入的系统实现相当灵活简洁。 (3)产生的效果 通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定DAOBean中所需的DataSource实例。DAOBean只需利用容器注入的DataSource实例,完成自身的业务逻辑,而不用关心具体的资源来自何处、由谁实现。 l 提高了组件的可移植性和可重用度 假设我们的部署环境发生了变化,系统需要脱离应用服务器独立运行,这样,由于失去了容器的支持,原本通过JNDI获取DataSource的方式不再有效(因为,现在则需要改变为由某个组件直接提供DataSource)。 我们需要如何修改以适应新的系统环境?很简单,我们只需要修改dataSource的配置: <beans> <bean id="dataSource" class=" org.apache.commons.dbcp.BasicDataSource " destroy-method="close">
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value> </property> <property name="url"> <value>jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=WebStudyDB</value> </property> <property name="username"> <value>sa</value> </property> <property name="password"> <value>1234</value> </property> </bean> <bean id="dataBean" class="examples.DAOBean"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> </beans> 这里我们的DataSource改为由Apache DBCP组件提供。没有编写任何代码我们即实现了DataSource的切换。 l 依赖注入机制减轻了组件之间的依赖关系 回想传统编码模式中,如果要进行同样的修改,我们需要付出多大的努力。因此,依赖注入机制减轻了组件之间的依赖关系,同时也大大提高了组件的可移植性,这意味着,组件得到重用的机会将会更多。 接着我会在讲一个更现实的生活中的例子。 对IoC的另一种解释示例---生活中找“对象” (1)控制倒(反)转 l 常规的方式---自己恋爱 举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。 l 借助于婚介(婚姻介绍所)找女朋友 那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。 (2)Spring所倡导的开发方式---由容器帮助我们管理对象的生命周期和关系 l 我们只需要将对象在Spring中进行登记 Spring所倡导的开发方式就是如此,所有的类都会在Spring容器中登记,告诉Spring你是个什么东西,你需要什么东西,然后Spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。 l 所有的类的创建、销毁都由Spring来控制 所有的类的创建、销毁都由Spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是Spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被Spring控制,所以这叫控制反转。 深入了解依赖注入 (1)IoC的实现前提---借助于依赖注入 IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。 比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了Spring我们就只需要告诉Spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A并不需要知道。 在系统运行时,Spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖Connection才能正常运行,而这个Connection是由Spring注入到A中的,依赖注入的名字就这么来的。 (2)如何实现依赖注入----通过reflection来实现DI 那么DI是如何实现的呢?Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,Spring就是通过反射来实现注入的。 利用下面的代码可以从配置文件中获得某个组件对象,并且动态地给该组件的message属性赋值。 Properties pro = new Properties(); pro.load(new FileInputStream("config.properties")); String actionImplName = (String)pro.get(actionBeanName); String actionMessageProperty = (String)pro.get(actionMessagePropertyName); Object obj = Class.forName(actionImplName).newInstance(); //BeanUtils是Apache Commons BeanUtils提供的辅助类 BeanUtils.setProperty(obj,"message", actionMessageProperty); return (Action)obj; Spring IOC与工厂模式的对比 IOC(Inversion of Control),译作反转控制,其功能是将类之间的依赖转移到外部的配置文件中, 避免在调用类中硬编码实现类,因此也被称作依赖注入(Dependency Injection)。 在以往的开发中, 通常利用工厂模式(Factory)来解决此类问题----使外部调用类不需关心具体实现类,这样非常适合在同一个事物类型具有多种不同实现的情况下使用。其实不管是工厂模式还是依赖注入,调用类与实现类不可能没有任何依赖,工厂模式中工厂类通常根据参数来判断该实例化哪个实现类,Spring IOC将需要实例的类在配置文件文件中配置。 使用Spring IOC能得到工厂模式同样的效果,而且编码更加简洁。 (1)用工厂模式来实现的示例 当我们在应用系统中的组件设计完全是基于接口定义时,一个关键问题便产生了-----我们的程序如何去加载接口的各个实现类。在传统的解决方案种往往基于Factory模式来实现。 l Product.java(代表某种产品类的接口,也就是我们所要创建的对象所应该具有的功能要求) public interface Product { public void execute(); } l 不同的产品类(也就是我们所要创建的各个对象) public class ConcreteProductA implements Product // ConcreteProductA.java { public void execute() { ... } } public class ConcreteProductB implements Product // ConcreteProductB.java { public void execute() { ... } } l Factory.java(工厂类,利用它来创建出不同类型的产品---客户所需要的对象) public class Factory { public Product CreateProduct(object param) { return ConstructObjects(param); } private Product ConstructObjects(object param) { ...//根据不同的产品类型的需求来创建不同的产品对象 } } l Client.java(调用类,也就是请求者类) public class Client { public Client() { Product product = Factory.CreateProduct(paramA); //实例化ConcreteProductA Product product = Factory.CreateProduct(paramB); //实例化ConcreteProductB ... } } 通过工厂模式,最终达到在ConstructObjects方法中设定实例化实现类的逻辑,这样对于调用类来说,不直接实例化实现类(工厂模式中工厂类通常根据参数来判断该实例化哪个实现类),纵然实现类发生变化,而调用代码仍然可以不作修改,给维护与扩展带来便利----系统中的其他组件需要获取这个接口的实现,而无需事先获知其具体的实现。 但采用工厂模式来实现时,将会有如下三个主要的缺点: l 除非重新编译,否则无法对实现类进行替换。 必须重新编译工厂类使得原本可以达成的易用性大大降低。在过去,Spring诞生之前,许多项目中,我们通过引入可配置化工厂类的形式,为这种基于接口的设计提供足够的支持。这解决了实例化的问题,但是它为我们的项目开发带来了额外的负担,同时,它也没有真正帮我们解决其余两个问题。 l 无法透明的为不同组件提供多个实现 这是我们在应用工厂模式时一个比较头疼的问题,因为Factory类要求每个组件都必须遵从Factory类中定义的方法和结构特征。 当然我们可以在代码的实现的形式上为Factory类中的ConstructObjects方法增加一个参数,通过该参数达到对接口实现的不同版本进行索引----这种实现方式的问题在于我们必须担负很大的维护工作量,每个组件都必须使用一个不同的关键字。从而使得它必须以一种与众不同的方式与其他组件的实例相区分。 l 无法简单的进行切换实例产生的模型----单例或者原形 上面的代码是实现了返回多个实例的方式,如果我们需要保持了一个Singleton的实例,此时我们必须需要重新修改并编译Factory类。 存在这个问题的核心是在于组件必须主动寻找接口的实现类,因此这个问题并不能通过传统的工厂模式加以解决。 (2)用Spring IOC实现的示例 l SpringConfig.xml <bean id="productA" class="ConcreteProductA" /> <bean id="productB" class="ConcreteProductB" /> l InitSpring.java public class InitSpring { AbstractApplicationContext wac = null; private static InitSpring instance = new InitSpring(); private InitSpring() { } public static void Init(AbstractApplicationContext wac) { instance.wac = wac; } public static Object getInstance(String objName) { return instance.wac.getBean(objName); } public static Object getInstance(Class objClass) { return getInstance(objClass.getName()); } } l Client.java(调用类) public class Client { public Client() { Product product = (Product)InitSpring.getObject("productA");//实例化ConcreteProductA Product product = (Product)InitSpring.getObject("productB");//实例化ConcreteProductB ... } } 对比调用代码,其中同样也没有硬编码实现类,但比较工厂模式,少了Factory类而且采用配置文件来决定各个产品的实现类,使用Spring IOC能得到工厂模式同样的效果,而且编码更加简洁、灵活方便。 Spring对于基于接口设计的应用造成了极大的冲击效应。因为Spring接过了将所有组件进行串联组装的重任,我们无需再纠缠于遍布各处的工厂类设计。 通过以上的这些通俗易懂的例子来解释Spring中的核心思想。不知你理解了吗?如果还是不太明白,不用担心后面的章节讲解会让你彻底明白的。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|||
返回顶楼 | |||