本系列的全部源代码及二进制文件可以从这里下载:IocInCSharp.rar éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
本部分示例代码请参考"src\Step3"目录éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
四、使用Spring.net实现依赖注入Spring在Java界可是响当当的名字,现在也有.net平台下的Spring框架了,那就是Spring.net。用户可以从http://www.springframework.net/下载到Spring.net的最新版本。本例子中使用的版本为“Spring Interim Build August 15, 2005 ”,并对Spring.Services组件中的Remoting部分做了微小调整,删除了代码中用于输出的部分命令。 éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
Spring.net为我们提供了一种基于配置文件的注入方式,目前Spring.net允许将值注入到属性,也允许将一个工厂绑定到属性,工厂的产品将注入属性;除此之外,Spring.net还允许将一个方法的返回结果绑定到属性;它还可以在绑定之前强制进行初始化。另外Spring.net还专门针对.net提供了Remoting以及Windows Service的“注入”方式。Spring.AOP允许完成横切(不过目前是调用的是AopAlliance的代码)。 éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
尽管我对基于配置文件的注入方式仍然有些偏见(我认为它很难Debug、难于理解、没有编译时错误校验、编写效率比较低。另外它还存在安全隐患 ,恶意用户可以借助修改配置文件将恶意代码注入系统。因此,Spring.net在Web开发中应当更具优势),但这并不能掩盖Spring.net的光芒(据说Castle比Spring.net要好,但目前我还没有尝试过使用Castle)。使用Spring.net,我们只需修改两三行代码,并提供相应配置文件,就可以轻松实现Ioc。应用Spring.net后,我们的系统依赖关系如下图所示:éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
  éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
从图中可以看出,MainApp、SayHello、HelloGenerator之间并不存在任何依赖关系,它们都依赖于抽象出来的接口文件。除此之外,MainApp还依赖于Spring.net,这使得MainApp可以借助Spring.net实现组件动态创建和组装。éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
对于原有代码,我们几乎不用作任何调整,唯一需要修改的就是MainApp中的调用方法,代码如下:éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
using System;using System.Configuration;using Spring.Context;namespace IocInCSharp{  public class MainApp  {      public static void Main()      {        try        {            IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext;            ISayHello sayHello = (ISayHello)ctx.GetObject("mySayHello");            sayHello.SayHelloTo("zhenyulu");        }        catch (Exception e)        {            Console.WriteLine(e);        }      }  }}首先我们要添加对Spring.Context命名空间的引用,然后解析配置文件的"spring/context"结点,得到一IApplicationContext对象(就好比在上一个例子中我们得到的ConfigInfo对象一样),剩下的事情就是向该Context索要相关的对象了(ISayHello)ctx.GetObject("mySayHello"),其中"mySayHello"由配置文件指定生成方式和注入方式。éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
配置文件的内容如下:éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
<?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">        <object id="mySayHello" type="IocInCSharp.SayHello, SayHello">            <property name="HelloGenerator">              <ref object="myCnHelloGenerator" />            </property>        </object>        <object id="myEnHelloGenerator" type="IocInCSharp.EnHelloGenerator, HelloGenerator" />        <object id="myCnHelloGenerator" type="IocInCSharp.CnHelloGenerator, HelloGenerator" />      </objects>  </spring></configuration>注意观察<object id="mySayHello".....>结点,就是由这里定义对象间相互依赖关系的。其中的<property name="HelloGenerator">结点定义了对什么属性执行注入,以及注入的内容是什么(<ref object="myCnHelloGenerator" />)。大家可以尝试将<ref object="myCnHelloGenerator" />改为<ref object="myEnHelloGenerator" />,看一看程序执行结果有什么变化来体会Spring.net的Ioc功能。éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
如果读者读到这里仍然觉得Ioc没有什么的话,那让我们再来看一个更为复杂的例子。在当前例子中,MainApp通过依赖注入调用了HelloGenerator的功能,但所有的调用都发生在本地。当前程序是一个地地道道的本地应用程序。现在如果要求在不更改一行代码的情况下,将HelloGenerator.dll放到另外一台计算机上,MainApp通过远程调用(Remoting)来访问HelloGenerator的功能。这似乎就有一定的难度了。éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
这么作并不是没有任何依据,其实Ioc除了可以实现依赖注入外,我们还应当看到它可以将我们从复杂的物理架构中解脱出来,专心于业务代码的开发。系统开发中关键是逻辑分层。在一个系统不需要Remoting时,开发的系统就是本地应用程序;当需要Remoting时,不用修改任何代码就可以将系统移植为分布式系统。Ioc使这一切成为可能。Rod Johnson在他的《J2EE without EJB》一书中有着详细的论述,很值得一读。据说该书的中文译本今年九月份出版(呵呵,就是这个月,不过我还没有看到市面上有卖的)。éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
为了能够更深入的分析在“Remoting”改造过程中我们可能遇到的麻烦,在后续两部分内容中,我们将分别介绍不使用Ioc的Remoting改造以及使用Ioc的改造,并比较两者之间的区别。éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%
(待续)éZµžn), Åwww.netcsharp.cnt”ôX°¬ðÒ%