App.config<?xml version="1.0" encoding="utf-8" ?><configuration> <configSections> <sectionGroup name="spring"> <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/> <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/> </sectionGroup> </configSections> <spring> <context> <resource uri="config://spring/objects"/> </context> <objects xmlns="http://www.springframework.net"> <description>一个简单的控制反转例子</description> <object id="PersonDao" type="Dao.PersonDao, Dao"/> </objects> </spring> </configuration>
复制代码 Program using System;using System.Collections.Generic;using System.Linq;using System.Text;using Dao;using DaoFactory;using Spring.Context;using Spring.Context.Support;namespace SpringNetIoC{ class Program { staticvoid Main(string[] args) { //NormalMethod(); // 一般方法//FactoryMethod(); // 工厂方法 IoCMethod(); // IoC方法" Console.ReadLine(); } privatestaticvoid NormalMethod() { IPersonDao dao = new PersonDao(); dao.Save(); Console.WriteLine("我是一般方法"); } privatestaticvoid FactoryMethod() { IPersonDao dao = DataAccess.CreatePersonDao(); dao.Save(); Console.WriteLine("我是工厂方法"); } privatestaticvoid IoCMethod() { IApplicationContext ctx = ContextRegistry.GetContext(); IPersonDao dao = ctx.GetObject("PersonDao") as IPersonDao; if (dao != null) { dao.Save(); Console.WriteLine("我是IoC方法"); } } } }
复制代码 一个简单的控制反转程序例子就实现了。
这样从一定程度上解决了Program与PersonDao耦合的问题,但是实际上并没有完全解决耦合,只是把耦合放到了XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中。我个人认为可以把IoC模式看做是工厂模式的升华,可以把 IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的。
依赖注入 1.控制反转(Inversion of Control)与依赖注入(Dependency Injection) 控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。 IoC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:<1>依赖查找(Dependency Lookup):容器提供回调接口和上下文环境给组件。EJB和Apache Avalon都使用这种方式。<2>依赖注入(Dependency Injection):组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。后者是时下最流行的IoC类型,其又有接口注入(Interface Injection),设值注入(Setter Injection)和构造子注入(Constructor Injection)三种方式。 图1 控制反转概念结构 依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造子或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:<1>查找定位操作与应用代码完全无关。<2>不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。<3>不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。 2.好莱坞原则 IoC体现了好莱坞原则,即“不要打电话过来,我们会打给你”。第一次遇到好莱坞原则是在了解模板方法(Template Mathod)模式的时候,模板方法模式的核心是,基类(抽象类)定义了算法的骨架,而将一些步骤延迟到子类中。 图2 模板方法模式类图 现在来考虑IoC的实现机制,组件定义了整个流程框架,而其中的一些业务逻辑的实现要借助于其他业务对象的加入,它们可以通过两种方式参与到业务流程中,一种是依赖查找(Dependency Lookup),类似与JDNI的实现,通过JNDI来找到相应的业务对象(代码1),另一种是依赖注入,通过IoC容器将业务对象注入到组件中。 3. 依赖查找(Dependency Lookup) 下面代码展示了基于JNDI实现的依赖查找机制。 publicclass MyBusniessObject{private DataSource ds;private MyCollaborator myCollaborator;public MyBusnissObject(){Context ctx = null;try{ctx = new InitialContext();ds = (DataSource) ctx.lookup(“java:comp/env/dataSourceName”);myCollaborator =(MyCollaborator) ctx.lookup(“java:comp /env/myCollaboratorName”);}…… |
代码1依赖查找(Dependency Lookup)代码实现 依赖查找的主要问题是,这段代码必须依赖于JNDI环境,所以它不能在应用服务器之外运行,并且如果要用别的方式取代JNDI来查找资源和协作对象,就必须把JNDI代码抽出来重构到一个策略方法中去。 4. 依赖注入(Dependency Injection) 依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责,“查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。 下面分别演示3中注入机制。 代码2 待注入的业务对象Content.java package com.zj.ioc.di;publicclass Content {</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> BusniessContent(){ System.</span><span style="color: rgba(0, 0, 255, 1)">out</span>.println(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">do business</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> AnotherBusniessContent(){ System.</span><span style="color: rgba(0, 0, 255, 1)">out</span>.println(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">do another business</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">); }
} MyBusniess类展示了一个业务组件,它的实现需要对象Content的注入。代码3,代码4,代码5,6分别演示构造子注入(Constructor Injection),设值注入(Setter Injection)和接口注入(Interface Injection)三种方式。 代码3构造子注入(Constructor Injection)MyBusiness.java package com.zj.ioc.di.ctor;import com.zj.ioc.di.Content; publicclass MyBusiness {private Content myContent;</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MyBusiness(Content content) { myContent </span>=<span style="color: rgba(0, 0, 0, 1)"> content; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doBusiness(){ myContent.BusniessContent(); } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doAnotherBusiness(){ myContent.AnotherBusniessContent(); }
} 代码4设值注入(Setter Injection) MyBusiness.java package com.zj.ioc.di.set;import com.zj.ioc.di.Content; publicclass MyBusiness {private Content myContent;</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setContent(Content content) { myContent </span>=<span style="color: rgba(0, 0, 0, 1)"> content; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doBusiness(){ myContent.BusniessContent(); } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doAnotherBusiness(){ myContent.AnotherBusniessContent(); }
} 代码5 设置注入接口InContent.java package com.zj.ioc.di.iface;import com.zj.ioc.di.Content; publicinterface InContent {void createContent(Content content);} 代码6接口注入(Interface Injection)MyBusiness.java package com.zj.ioc.di.iface;import com.zj.ioc.di.Content; publicclass MyBusiness implements InContent{private Content myContent;</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> createContent(Content content) { myContent </span>=<span style="color: rgba(0, 0, 0, 1)"> content; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doBusniess(){ myContent.BusniessContent(); } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> doAnotherBusniess(){ myContent.AnotherBusniessContent(); }
} 5.依赖拖拽(Dependency Pull) 最后需要介绍的是依赖拖拽,注入的对象如何与组件发生联系,这个过程就是通过依赖拖拽实现。 代码7 依赖拖拽示例 publicstaticvoid main(String[] args) throws Exception{//get the bean factoryBeanFactory factory = getBeanFactory(); MessageRender mr = (MessageRender) factory.getBean(“renderer”); mr.render(); } 而通常对注入对象的配置可以通过一个xml文件完成。 使用这种方式对对象进行集中管理,使用依赖拖拽与依赖查找本质的区别是,依赖查找是在业务组件代码中进行的,而不是从一个集中的注册处,特定的地点执行。 |