对话Spring.NET
Aleksandar Seovic是Solutions for Human Capital公司的创始人和总经理,该公司专注于企业文档和内容管理领域的软件开发及咨询。他熟悉Java和.NET平台,曾经为全球财富500强的公司主持开发了众多项目,这些公司主要来自医疗与金融行业。
Aleks是Spring.NET这个开源的.NET应用程序框架的领头人之一,并且是这个框架中的Web、AOP和Services(服务)模块的领头开发人员。
Mark Pollack是CodeStreet, LLC的合作伙伴,这是一家为金融行业提供软件开发和咨询的公司。他曾先后担任多种前端办公交易系统(front office trading systems)的架构师和开发人员,这些系统综合使用了.NET和Java技术,并主要运用了基于消息的中间件。在开始他的软件开发生涯之前获得了物理学博士学位。
Mark从2003年以来一直参与了Spring框架的开发,并在2004年创建了Spring的.NET版本Spring.NET。
InfoQ:Spring.NET究竟是什么?为什么.NET社区需要使用它?
Aleks:Spring.NET是一个开源的应用程序框架,它可以极大地简化.NET应用程序开发,甚至于很多功能如果没有它的支持,将非常难以实现。它同时也是 Java平台下流行的Spring框架的“亲弟兄”。但是,如果说Spring.NET仅仅是对Spring的移植,则有欠公允。事实上,我们在移植 Spring时,更注重对Spring精神的体现,而不是简单的代码移植。
在我早在2003年开始使用Spring时,我很快就认识到它能够使得我在编写 Java应用程序时更加快捷,使我的程序代码更加小巧、干净,更具有灵活性,因而易于测试、维护和扩展。在我使用了依赖注入以及Spring提供的其它出色功能时,我就情不自禁地希望找到提供类似功能的基于.NET平台的框架,然而却没有能够找到。大约在同一时间,在2004年的早期,Mark经历了相似的“寻找历程”,从而开始了Spring.NET项目的开发。当我听闻这一消息后,就立即加入了这个项目,开始对核心的依赖注入容器以及AOP框架进行移植。
Mark:我与 Aleks首先达成共识的一点就是Spring.NET不能盲目地对Spring的代码进行移植。Spring隐含的驱动力是创建了一个框架,它可以降低 J2EE开发的复杂度,并提供一个简单的、基于POJO的编程模型,这一编程模型允许Spring应用程序运行在任何J2EE容器上。显然,J2EE与. NET平台并不相同,它们面临的问题也是不相同的,如果只是试图按照特性对特性的方式将Spring移植到.NET,就无异于“买椟还珠”了。而且,. NET平台自身还存在一些不足,因此我们决定将Spring.NET的关注点放在解决这些不足上,以提高.NET开发人员的生产效率,使得他们能够编写更好、更小以及更干净的代码。开发者可以只关注于业务逻辑,而非基础设施相关的内容。
Aleks:Spring的其中一些核心功能,例如依赖注入容器和AOP框架,是可以通用并且完全独立于平台的,我们对此只是照猫画虎地对代码进行了移植。另一方面,我们认识到ASP.NET的实现方式打破了常规,它的强大超过了Java 中的servlets和JSP,因此我们认为移植Spring MVC框架是没有价值的。相反,在Spring.NET中,我们选用的Web框架只是对ASP.NET的封装与扩展。
同样,还有一些组件例如Spring Remoting,它允许开发者使用一些特定的Java技术接受任意的POJO对象,并将其作为远程对象输出。类似于这样的情况,我们希望能够将它的理念应用到.NET中,但是由于在.NET中对于远程技术的处理方式是不相同的,我们不得不推倒重来。
整体而言,我们的目标不仅仅是要保留Spring的功能特性与实现,而且还要保留那些使得Spring如此普及的闪光之处,这些特性能够针对.NET平台存在的问题提供Spring风格的解决方案。
InfoQ:你们在谈话中多次提到了依赖注入。可否解释一下它的确切含义,以及为什么.NET开发人员需要关注它?
Aleks:好的。依赖注入实际上是个非常简单的概念。当你审视一个典型的应用程序时,你会看到对象之间存在许多依赖。表示层对象会依赖于服务层对象,而这些服务层对象又依赖于其它服务对象和数据访问对象等。在一个运行中的应用程序中,会有许多彼此依赖的对象纠缠在一起。
获得一个依赖的最简单方式就是直接调用构造函数创建对象。对象虽然可以被创建,但却存在几个问题:类很难独立测试,如果不修改创建它的代码,就不可能改变它使用的依赖类。这些问题大多数都可以使用某种配置工厂,ServiceLocator或者Registry去创建或查找一个对象来解决。这种方法我们称之为依赖查询(Dependency Lookup),如果实现正确,会收到很好的效果,但是工厂、Service Locator或Registry自身引入的依赖却没有办法解决。它需要做比依赖注入更多的工作。
另一方面,依赖注入意味着一个对象的依赖关系可以通过外部“注入”。不需要为对象查找任何内容——它只是简单地为依赖关系声明了私有字段,允许它们能够被外部提供,提供的方式可以是构造函数的参数或者通过属性的设置器(setter)。这使得对象非常简洁,具有可用性。如果依赖是通过接口声明而非确定的类,同样可以使得相关对象非常易于进行单元测试。
如果你了解了这点,那么对于依赖注入而言就没有什么新颖之处了。无论你是为IDbCommand实例设置Connection属性,还是设置Form实例的Menu属性,或者将 Socket实例作为NetworkStream构造函数的参数进行传递,你都可以手动地注入依赖。我在一些旧有技术例如VB6或者COM中发现了相似的例子。
然而,使得依赖注入在最近几年变得如此普及的原因在于轻量级容器的崛起,这些轻量级容器包括Java中的Spring、Pico 和HiveMinde,以及.NET中的Spring.NET、Castle和最近微软的ObjectBuilder。这些容器允许你在应用程序中配置对象配线规则(object-wiring rules)[译注:即把对象连在一起以便它们相互之间能够通信],然后基于这些规则创建应用程序的对象,并将它们连接在一起,从而为开发人员提供自动的依赖注入。不同的容器允许指定的对象配线规则是不相同的,但是最终的目的与结果通常则是相同的。
容器所能够提供不仅限于此。因为他们对管理的所有对象了如指掌,这就使得在运行中的应用程序中管理对象变得轻而易举。我不确定其他容器是否能够做到,但是Spring和Spring.NET都能够提供钩子(hooks),使得开发者能够在一个对象生命周期的不同状态中执行定制的逻辑,非常的行之有效。
InfoQ:那么Spring.NET相对于其他你所提及的其他框架而言,有什么优势呢?
Aleks:好吧,让我们首先比较一下Spring.NET与ObjectBuilder,因为这样的比较相对容易。ObjectBuilder只是一个轻量级DI容器,而Spring.NET则拥有其它的功能特性,从而使得它成为一个不错的选择。DI容器只是这些组件中的一个,虽然它是Spring.NET中非常重要的一个。
即使针对纯粹的DI功能,Spring.NET也有着远超ObjectBuilder的功能特性,而且它并不具有侵入性。这意味着你的类可以完整地保留与Spring.NET的独立性,即使是通过Spring.NET对这些类进行配置。反过来, ObjectBuilder却具有很强的侵入性——它使用attribute来指定是哪一个工厂类被用来获取特定的对象,你必须遵循预先定义的准则编写这些工厂类。整体而言,编写ObjectBuilder的目的显然是为了支持与Composite UI Application Block的配置,而不是通用的、可重用的DI容器,甚至微软的Patterns & Practices小组也是如此认为的。
Spring.NET的另一个巨大优势在于,ObjectBuilder只能用于.NET 2.0应用程序,而Spring.NET则能够用于.NET 1.x和2.0的应用程序。
对比Spring.NET与Castle相对困难一些。它们都是提供了DI容器的框架,也包含了其他组件,例如Web框架、数据访问框架等。我对Castle的DI容器了解不深,无法基于基本的功能点对Spring.NET和Castle进行点对点的比较,但是我知道Castle的Web框架以及数据访问框架与我们所实现的略有不同。
究竟是选择Spring.NET还是Castle,我想这需要根据开发人员的技术背景以及特定需求来下判断。就我所知,使用Ruby On Rails的开发人员更倾向于使用Castle,如果开发人员来自Java阵营,特别是他们在此之前使用过Spring,则更倾向于Spring.NET。我并不确定哪一种框架更适用于.NET开发人员,但我建议他们最好两者都能够尝试一下,然后选择其中更适合的一个——做出选择远甚于全部放弃。
最后我认为,比较ObjectBuilder与Castle而言,Spring.NET DI容器还有一个优势在于,它非常容易在真实的应用程序中进行测试与验证。因为我们继承了Spring的DI容器实现,因而可以从Spring的测试与产品使用中获益。
InfoQ:Spring.NET适用于哪些场景?
Aleks:Spring.NET可以应用于许多场景。核心特性如依赖注入(DI)容器、AOP以及数据访问框架,都可以非常好地应用在任意的.NET应用程序中。它使得你能够创建更简单、更干净的应用程序。为了尽量消除它所带来的副作用,开发人员还可以免费获得功能强大的配置机制。
如果你正在编写一个ASP.NET Web应用程序,你可以考虑将Spring.NET Web框架(Spring.Web)加入其中。因为它是ASP.NET的一个扩展,所以不会改变ASP.NET开发人员的开发方式,同时又为他们提供了一些新的强大功能。例如,它支持web pages与user controls的依赖注入,这本身就具有充分的理由来选择使用它。然而,最重要的是我们增加了双向数据绑定、进程与状态管理,以及经过改善的本地化支持和增强的数据验证框架。最后,Spring.Web还为ASP.NET 1.1开发人员提供了某些只有在ASP.NET 2.0下才能使用的很酷的新特性,例如Master Pages,它不仅改善了ASP.NET 1.1的开发体验,而且还使得从1.1到2.0的迁移变得更加地容易。
Spring.NET Services Framework(Spring.Services)支持开发者接收一个普通的接口/实现对象,然后以远程SAO或者CAO对象、Web服务甚至当作一个服务组件输出。然后,通过为输出的远程服务应用方面(aspects),可以实现安全检查、加密、压缩等超级酷炫的事儿。
Spring.Services的另一个组件是客户端代理生成器,它会提供一个普通的客户端接口以及一个远程终结点(remote endpoint)的URL,从而为上面提及的任何一种服务类型的远程服务创建动态代理。例如,你可以使用WebClientFactory类在运行时动态创建一个Web服务代理。通过这种方式创建一个客户端Web服务代理,比之于在VS.NET中通过使用Web引用创建,优势更为明显。首先,你可以提供一个代理需要实现的接口,并通过该接口从客户端应用程序中访问代理。其次,由于接口为你的Web服务方法定义了所有的参数与返回类型,因而在你真正需要使用现有的数据类型时,不必处理VS.NET生成的伪类型。而这可能是在VS.NET中使用Web引用所带来的最大困扰之一。
最重要的是,你甚至可以创建一个代理,该代理可以通过我们对IIOP.NET的集成,调用Spring或者任意EJB容器中的EJB所管理的远程Java RMI对象。目前,这个版本还有一些与序列化相关的功能没有实现,通过这些功能可以实现真正的客户端位置的透明化,我们希望在下一个版本中增加。
Mark:Spring.NET的数据访问框架(Spring.Data)为‘普通的’ADO.NET提供了许多颇有价值的修饰。其中一个关键组件就是事务管理的抽象,它允许开发者轻松地在不同的事务策略之间切换,例如普通的旧的ADO.NET,EnterpriseServices分布式事务以及新的System.Transaction命名空间。所有的事务策略都可以通过嵌入的特性或者非侵入性的XML配置来完成。我们对于ADO.NET框架的扩展使得 ADO.NET的操作更简单,并且考虑了连接/事务的资源处理——否则在通常情况下,就必须增加开发人员的工作量,即使使用System.Transactions命名空间。
Spring.NET还有几个组件可以脱离于框架单独使用,例如expression language(表达式语言),它可以很好地消除在应用程序中编写与反射相关代码的需要。
当你决定是否在特定应用程序中使用Spring.NET时,切记不要将Spring.NET当作一个“整体不可分割”的框架。当你一起使用它们的时候,它们只是一个框架的集合,能够更大程度地完成协作,但也完全可以彼此独立地单独使用。
InfoQ:人们如何在.NET社区中获得Spring的概念?用户的发展情况如何?目前的下载次数以及其他统计数据分别是多少?
Aleks:用户的反馈非常踊跃。在Java中使用过Spring的开发人员对此雀跃不已,因为在他们转移到.NET平台进行应用程序开发时,能够使用相同的编程模型,获得相同的益处。从未使用或听说过Spring的开发者一旦认识到使用Spring.NET可以使得他们的代码更加简单,更加灵活,就会很快被依赖注入以及Web框架所吸引。根据论坛所反映的情况来看,依赖注入容器与Web框架最为瞩目。
在开发人员试用了Spring.NET之后,我们收到了许多积极的反馈与建议,例如,“没有Spring.NET,我都不会构建应用程序了” 或者 “我希望尽快地掌握它”。像这样的一些评论确实在驱动着我们继续前进,同时验证我们之前所做的工作。
当然,我们每一次都会收到一些问题,例如:“我能够在Spring.NET中获得哪些在.NET 2.0/ASP.NET/EntLib/...中没有的东西?” 对于这些问题,我们都会尽可能坦诚而又全面地回答,并鼓励开发人员自己去尝试Spring.NET——一旦他们开始使用Spring.NET,就能一目了然地看到它所带来的好处。
至于商业用户,我们知道华尔街的一些大型公司以及欧美的一些知名石油企业对此颇有兴趣。这这些公司中,有一些本身就是Spring的用户,目前又正在寻找经过交叉培训的开发者。由于不同平台都保留了这种编程模型,因而Spring/Spring.NET的组合就格外值得关注了。其余的商业用户则是那些希望简化他们开发的.NET开发团体。我们曾经与一个来自全球知名的石油企业的Spring.NET用户有过联系,他告诉我们他们正在使用Spring.NET作为企业级开发平台的基础框架。这真的让我们感到非常鼓舞。
Oracle咨询部门正在他们的项目中使用Spring.NET数据访问框架,他们以及一些开展Spring.NET培训的咨询公司也从中取得了一定的收获。
在其他领域中,我们还看到软件开发公司对Spring.NET给予的极大关注。我们已经与各种软件销售商展开了大量的讨论,这些销售商正在使用Spring.NET以支撑他们的产品。Spring.NET帮助他们缩短了产品上市的时间,减少了他们必须编写与维护的代码量。
Mark:至于统计数据,虽然很容易就可以获得,却很难说得清楚,因此我不打算作这样的尝试。目前,我们在SourceForge上每个月有2000个下载,但是实际的数字可能更高,因为在过去的3-4个月期间,我们通告了最新的每夜构建[译注:原文为nightly build,应该代表daily build的谐意]版本可以直接在我们的网站上下载。根据论坛软件的统计,我们拥有800个注册论坛用户以及超过100个活跃用户。FishEye为我们展示的代码行数在过去的两年半时间内,已经稳步地增长到了超过50万行。这还是基于一个前提,那就是我们对于代码行数始终抱有谨慎的态度,只要有机会,我们都会努力去除那些无用的代码。我们应该抱着怀疑的态度来看待这个数字,因为在报告的代码行数中,有80%都是单元测试的代码以及文档。 Spring.NET实际上相当的轻巧:核心程序集包含了核心的DI容器,表达式语言,验证框架以及大量的配置与工具类,合计只有440KB。AOP框架与Web框架的二进制文件则更小,分别只有96KB和88KB。
当然,这些数字相比较Spring而言,就未免黯然失色了。自从Spring的2.0版本在数周之前发布以来,就有超过50,000个下载,它也被纽约和伦敦以及其他城市的许多金融部门与银行所使用。但是,我们必须考虑如下现实:Spring自从2003年问世以来,有大量的书籍对其进行了介绍,甚至还有两次专题会议全面对其进行研讨,一次是在美国 ,另一次是在欧洲 。另一方面,大多数Spring.NET代码仍然处于预发布状态,只有核心的DI容器和AOP在2005年末发布了一个正式版。今天的对话是我们第一次面向广大受众谈论Spring.NET。考虑到这些因素,我们认为能取得这样的成果已经相当不错了。最重要的是,基于目前我们收到的反馈,我们知道自己前进在正确的道路上,随着.NET开发人员对Spring.NET的逐渐了解,并认识到它在真实项目中的价值,我们相信会有越来越多的用户接受它。
InfoQ:能否介绍一下在真实的项目开发中,那些基于Spring.NET开发成功或失败的用户的看法?
Aleks:对于一个开源项目而言,很难获得这一类的信息。我们根据自身的经验以及论坛的反馈,了解到有大量的成功项目,但是人们通常不愿意宣告自己项目的失败。
毋庸置疑,会有一些失败的案例存在。毕竟,大多数IT项目仍然在失败,这让人很沮丧,却是不可争辩的事实。然而,问题是什么导致了项目的失败?从我个人的经验以及我所听到的项目失败经历来看,技术自身通常都不是项目失败的真正原因。即便如此,也完全有可能是某人在不应该使用Spring.NET的地方使用了Spring.NET,从而导致项目失败。但是我们又要看到,如果我用锤子代替灯管去使用,那么究竟是谁的责任:锤子还是我?
Spring.NET仅仅是工具箱中的一件工具而已。在选择使用的工具时,只要开发者审慎地评估他们的需求,项目就没有理由会因为技术的原因而失败。遗憾的是,开发者尤其是缺乏经验的开发者,总是倾向于使用“一件新鲜玩意儿”去解决所有问题。这种情况只有通过教育培训以及经验积累来解决。
InfoQ:Spring.NET最近的进展如何?
Mark:我们首先要做的是发布最终的1.1版本。该版本包括一个经过改善的DI容器与AOP,以及Web、Services和数据访问框架的正式版本。遗憾的是,在最近几个月中,我和Aleks都在忙于自己的工作,无法投入我们希望分配给Spring.NET的时间。此外,Spring.NET的两个核心开发人员,Bruno Baia和Erich Eichinger,已经修改了我们留下的大多数bugs,并在诸多方面进行了行之有效的改善。我们可能会在不久的将来发布一个RC版本,并在年底发布最终的1.1版本。Bruno与Erich一定会非常乐意看到1.1版本的“新鲜出炉”。
一旦一切就绪,我们还需要介绍关于使用Spring.NET更多的信息。我们会撰写相关的文章,在会议和用户组聚会上进行演讲,不用说,我们还会为Apress撰写书籍,这是我们很早以前就欠下的书债,以至于我们都不敢与我们的编辑联系了。
当然,总会有一些很酷的新东西,我们会计划加到未来的版本中……
InfoQ:未来版本的计划是怎样的?
Aleks:不少工作都很难决定从何处开始……首先,总是需要改进现有的内容,我们会为核心DI容器、AOP、Web框架以及其他将会作为1.1版本发布的组成部分添加新的功能特性。至于目前还在我们考虑之中的新特性,我们正在计划为Windows应用程序增加一个框架,它可以为Windows Forms的开发人员提供类似于Spring.NET Web框架为ASP.NET开发人员提供的功能。对此,一些工作已将开始,而有的内容则已经完全完成了。例如数据验证框架。目前,它已经可以被用到Web框架中,而且也可以无需任何修改应用到Windows应用程序中,因为它与表示逻辑完全无关。
Mark:此外,我们还计划完成众多的集成。我们已经拥有如下的集成:集成NHibernate实现持久化,集成Anthem.NET与Atlas实现AJAX,集成IIOP.NET实现与Java的互操作,以及为Web服务集成WSE2/WSE3。还有与某些EntLib功能模块之间的集成在概念上已经得到了验证,例如EntLib中的缓存特性,它支持将ASP.NET Cache或者Caching Application Block当作方法结果的缓存。然而,还有大量的集成工作需要完成,这些集成工作可以大大地简化基于Spring.NET的应用程序对各种工具与技术的使用。
我们正在计划实现与其他用途广泛的工具的集成能力,例如:实现持久化的Wilson ORM与iBatis.NET,针对消息传输的TIBCO、MSMQ以及MQSeries,还有与所有EntLib功能块的完整的、达到产品质量的集成……在互操作方面,还有许多令人激动的特性会被实现,我们正努力消除平台的边界性,并使得Spring.NET与Spring管理的Java远程服务之间进行相互调用成为可能。我们希望这种交互尽可能地像在一个单独平台上进行交互那样简单。我们还将提供对BizTalk的集成,对SharePoint的支持等。一言以蔽之,我们将根据用户的需求来决定我们需要集成哪些产品和工具,并排定实现他们的优先级。毋庸置疑,这其中的一些集成实际上就是来自于用户本身。
Aleks:此外,随着微软推出的众多令人激动的技术与产品,我们也会尽快实现改进与集成。当我正在关注FishEye的统计结果时,我想Bruno[译注:Spring.NET的一位项目成员]已经将WPF的glue code[译注:glue code是为了连接一个可以重用,并且已经存在的应用程序而编写的代码。例如有两个程序包A和B,A和B之间没有任何依赖关系,如果希望在程序中让A和B之间进行协作,就需要编写glue code。glue是胶水的意思,这里取用了粘贴剂的隐喻]提交到了sandbox中。同样还有Windows Communication Foundation,它可以使得我们的远程能力更加强大;Workflow Foundation则可以为我们的Web框架在进程管理方面提供改进。我们能够持续跟进,但关键是有太多令人激动的新技术与产品如“雨后春笋”一般诞生,我相信接下来的几年将是整个Spring.NET团队充满艰辛与乐趣的时期。希望在这段时间内,我们的用户能够多一点乐趣,多一些成果,少一些艰辛与困难:-)
InfoQ:如果我对Spring.NET很有兴趣,那么我应该从哪里开始?
Mark:最佳起点当然是我们的网站www.springframework.net,在那里你能够找到下载区。在接下来的几周时间内,我们力图发布3个预览版本以及1个RC版本,但是在这期间,你最好是下载我们最近的每夜构建版本。在下载包中包含了许多范例,我们鼓励用户仔细地阅读它们,以明确项目使用了Spring.NET特性,会有何不同。
你还可以找到文档区的链接,以及用户论坛,这些都是寻求帮助的最佳位置。我们希望很快能够在那里见到你:-)
查看英文原文:Spring.NET - QnA