【资料整理】Spring.Net框架介绍(1)

       在开始介绍Spirng.Net之前,有必要先介绍一下相对应的两个Java开源框架Hibernate和Spring。它们是在Java的Web应用开发中已经很成熟的框架,而前者则是个还不成熟的.Net中的开源框架,它们在SourceForge开源社区中正在得到不断的补充和完善,其最终目标就是要在.NET环境下实现Hibernate和Spring的全部特性。

 

       Spring是一个解决了许多在J2EE开发中常见问题的强大框架,它提供了管理业务对象的一致方法并且鼓励注入对接口编程而不是对类编程的良好习惯。Spring提供了唯一的数据访问抽象,包括简单和有效率的JDBC框架,极大改进了效率并且减少了可能的错误。Spring的数据访问架构还集成了Hibernate和其他O/R mapping解决方案。Spring还提供了唯一的事务管理抽象,它为各种底层事务管理技术,例如JTA或者JDBC事务提供一个一致的编程模型。Spring提供了一个用标准Java语言编写的AOP框架,它给POJO提供了声明式的事务管理和其他企业事务。这个框架足够强大,使得应用程序能够抛开EJB的复杂性,同时享受着和传统EJB相关的关键服务。Spring还提供了可以和IoC容器集成的强大而灵活的MVC Web框架。

Hibernate是一个开放源代码的对象关系映射(Object-Relation Mapping,ORM)框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲地使用对象编程思维来操纵数据库。Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。鉴于Hibernate在Java社区巨大的影响力,它其实已经成为Java社区的持久层技术事实上的标准。

 

       基于.NET的开发框架NHibernate和Spring.Net分别来源于上述提到的两个Java框架,框架结构和实现功能都基本一致,只不过应用环境从Java转到了.NET。到目前为止NHibernate和Spring.NET仍然处于完善阶段,不是特别成熟稳定,但是具有Hibernate和Spring的强大背景,使得它们有着很光明的未来。事实上,现在NHibernate和Spring.NET正在被越来越多的开发人员所熟知,并且有些开发人员已经开始尝试在自己的项目中使用这两个框架。

  1.1.1企业开发框架的优势

      在当今的企业环境中,开发企业应用程序是一个相当繁琐、浪费时间的过程,为了让这个过程变得相对简单且高效,一些开源的、轻量级的框架正在被广泛的使用,比如Java下的Structs、Spring、Hibernate、iBATIS以及.NET下的Spring.Net和NHibernate等。它们的广泛使用降低了系统开发的复杂度,弱化了系统各模块间的耦合程度,缩短了开发周期,增强了系统的可维护性和可扩展性。“站在巨人的肩膀上”是牛顿的一句名言,它已经成为google学术搜索引擎的座右铭,同样也为笔者所推崇。笔者对.Net软件开发平台的设想如下,采用开源的ORM框架做数据持久层,ASP.NET没有合适的Web层框架,采用Asp.Net的Code-behind方式编写代码,数据持久层同Web表现层之间的连接采用IoC容器。

衡量了多种框架以后,最终笔者推荐采用的技术架构就是Hibernate+Spring.Net+ASP.NET,下面对NHibernate和Spring.net分别进行比较详细的介绍。

 

     1.1.2   Spring.Net介绍

 

      Spring.Net是一个关注于.NET企业应用开发的应用程序框架,它能够提供非常丰富的功能,例如依赖注入(Dependency Injection),面向方面编程(Aspect Oriented Programming),数据访问抽象以及ASP.NET集成等。Spring.NET脱胎于Java的Spring框架,其1.0版包括了一个功能完整的反转控制容器和AOP函数库,在后续的版本中将包含对ASP.NET、Remoting和数据访问的支持。Spring.Net的框架如图1-53所示。

图1-1    Spring.Net的框架图

1.控制反转

 

    控制反转(IoC,Inversion of Control)意味着将设计好的交给系统去控制,而不是在自己的类内部控制。

IoC是近年来兴起的一种思想,主要是协调各组件间相互的依赖关系,同时大大提高了组件的可移植性,组件的重用机会也变得更多。在传统的实现中,由程序内部代码来控制程序之间的关系。我们经常使用new关键字来实现两组键间关系的组合,这种实现地方式会造成组件之间耦合(一个好的设计,不但要实现代码重用,还要将组件间关系解耦)。IoC很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。

 

    分离关注(Separation of Concerns:SOC)是产生IoC模式AOP的最原始动力,通过功能分解可得到关注点,这些关注可以是组件、方面或服务。GOF设计模式中,我们已经习惯一种思维编程方式——接口驱动。接口驱动有很多好处,可以提供灵活的子类实现,增加代码稳定和健壮性等,但是接口一定是需要实现的,即如下语句一定要执行。

AInterface a = new AInterfaceImp(); AInterfaceImp是接口AInterface的子类,Ioc模式可以根据需要延缓接口的实现,有个比喻:接口如同空的模型套,在必要时向模型套注射石膏,成为一个模型实体。可以人为控制接口的实现完成“注射”。

 

    IoC的实现方式有以下几种:

*      基于接口的(Interface-based IoC,Type-1)。

*      基于设值的(Setter-based IoC,Type-2)。

*      基于构造的(Construtor-based IoC,Type-3)。

 

下面通过简单的例子分别介绍上述几种实现方式。

(1)Type-1。基于接口的设计方法通常是利用接口将调用者与实现者分离。

 

#001    public class Sport {

#002    private InterfaceBall ball; //InterfaceBall是定义的接口

#003    public void init() {

#004    //Basketball实现了InterfaceBall接口

#005    ball = (InterfaceBall) Class.forName("Basketball").newInstance();}

#006    }

 

Sport类在编译期依赖于InterfaceBall的实现,为了将调用者与实现者分离,可以动态生成Basketball类并将其强制类型转换为InterfaceBall。

 

(2)Type-2。基于设值的设计方法是通过在类中暴露setter方法来实现依赖关系。

 

#001    public class Sport {

#002    private InterfaceBall _ball;

#003    public InterfaceBal1 ball

#004    {

#005        set{ _ball = value ;} }

#006    }

 

Spring.NET就是实现了该类型的轻量级容器。

 

(3)Type-3。通过构造方法完成依赖关系。

 

#001    public class Sport {

#002    private InterfaceBall ball;

#003    public Sport(InterfaceBall arg) {

#004    ball = arg; }

#005    }

 

由于Type-3在构造期就形成了对象的依赖关系,所以对对象的重用变得困难。有些框架需要组件提供一个默认的构造方法,此时就显现出Type-3的局限性。通常所有的参数都是通过构造方法注入的,当对象间的依赖关系较多时,构造方法就显得比较复杂,不利于单元测试。PicoContainer就是实现了Type-3依赖注入模式的轻量级容器。

 

2.Spring.NET库

 

     Spring.NET库有6个基本组成部分,基本上涵盖了Spring.NET框架的所有功能结构。

 

*      Spring.Core库是Spring.NET框架最基础的部分,它提供了依赖注入的功能。Spring.NET中大部分的函数库都依赖于这个核心库提供的功能,或者是对核心库的扩展。IObjectFactory是核心容器接口,负责管理容器内的注入对象,而IApplicationContext则是IObjectFactory的继承,它扩展了一些功能。

 

*      Spring.Aop库为商业逻辑对象提供了面向方面编程的支持,它为创建企业应用和为商业对象提供服务打下了基础,是Spring核心库中IoC容器的补充。

 

*      Spring.Web库为ASP.NET增加了很多功能,例如ASP.NET页面的依赖注入,数据双向绑定,为ASP.NET提供母版页功能,增强了本地化支持。所有的这些都是对ASP.NET很好的扩展。

 

*      Spring.Services库可以将任何一个“普通”对象(“普通”是指该对象不是继承自特殊服务的基类)暴露成为一个企业应用(COM+)或者远程对象。因为对依赖注入和原数据属性重载的支持,.NET 中的Web服务会获得更好的配置上的灵活性。同样,该库也提供了对Windows服务的支持。

 

*      Spring.Data库为.NET提供了一个数据访问层的抽象,它能够用于从ADO.NET到多种ORM Provider的数据访问提供者。它同时包含了一个ADO.NET抽象层,简化了对ADO.NET的编码和事务管理。

 

*      Spring.ORM库提供了一个用于常见的对象—关系映射库的综合层,它提供了诸如对事务管理的支持等功能。

 

3.面向方面编程(AOP)

 

     面向方面编程是对面向对象编程(OOP)的补充,是另外一种思考编程框架的方法。面向对象是将应用分解成具有层次结构的对象;而面向方面编程则是把程序分解成方面或者关注点,使诸如事务管理等方面的模块化成为可能。Spring.NET中很关键的一个组件就是AOP框架。能够帮助Spring.NET的IoC容器为企业应用提供一个非常强大的中间件解决方案。

AOP用于Spring.NET可以完成下列功能。

 

*      提供公开的企业服务,尤其是作为COM+公开服务的替代者。这些服务中最重要的服务是公开的事务管理,这是Spring.NET事务抽象的基础。

 

*      允许用户实现定制的方面,通过面向方面编程来补充面向对象编程的不足。

用户不但可以把Spring.NET AOP看作是能够不通过COM+就可以提供公开事务管理的技术,而且还可以充分发挥Spring.NET AOP框架的功能区实现定制方面。

 

通过上面的介绍读者可能对AOP已经有了一个大致的了解,下面介绍几个关于AOP的概念。

 

*      方面(Aspect):这个是一个让读者感觉比较模糊的概念,它和通常意义上的方面不完全一样,它是对关注点的模块化,这可能会横切多个对象。事务管理是一个非常好的横切关注点企业应用的例子。在Spring.NET中,方面作为建议者或者监听器的形式实现。

 

*      连接点(Jointpoint):程序运行期间的一些点,例如方法调用或者特殊的异常被抛出。

 

*      建议(Advice):AOP框架在一个特殊连接点上采取的动作。这些不同类型的建议包括“around”、“before”和“throws” 等建议。很多AOP框架,包括Spring.NET,都把一个建议模拟成一个监听器,同时维护一个“around”连接点的监听器链。

 

*      切点(Pointcut):一组连接点,用于指定建议应该激活的时间。一个AOP框架必须能够允许开发人员指定切点,例如,使用正则表达式。

 

*      介绍(Introduction):添加方法或域到建议类。Spring.NET允许介绍一个新的接口到任何一个建议对象中。例如,为了简化对对象状态变化的跟踪,可以使用建议为任何对象实现一个IAuditable接口。

 

*      目标对象:包含连接点的对象。

 

*      AOP代理:由AOP框架创建的对象,包括建议。在Spring.NET中,一个AOP代理是一个在运行期使用IL代码生成的动态代理。

 

*      Weaving:装配对象创建一个被建议对象。装配工作发生在编译期(例如使用Gripper-Loom .NET编译器),也能发生在运行期。Spring.NET在运行期执行装配动作。

 

4.Spring.NET应用实例

 

     下面以经典的Movie Finder作为Spring.NET应用实例来讲解IoC容器的使用方法。实例的C#代码可以在Spring.NET发布版的examples/Spring/Spring.Examples.MovieFinder目录中找到。

 

(1)Movie Finder。

   MovieFinder例子的起始类是MovieApp类,这是具有单一应用程序入口点的普通.NET类。代码如下所示:

 

#001    using System;

#002    namespace Spring.Examples.MovieFinder

#003    {

#004        public class MovieApp

#005        {

#006             public static void Main ()

#007             {

#008             }

#009        }

#010    }

 

现在想做的是获得一个对MovieFinder类实例的引用。这是Spring.NET例子,所以要从Spring.NET的IoC容器类IApplicationContext获得这个引用。应用程序配置文件中的IApplicationContext配置信息如下:

 

#001    <?xml version="1.0" encoding="utf-8" ?>

#002    <configuration>

#003    <configSections>

#004    <sectionGroup name="spring">

#005    <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>

#006    <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />

#007    </sectionGroup>

#008    </configSections>

#009    <spring>

#010    <context>

#011    <resource uri="config://spring/objects"/>

#012    </context>

#013    <objects>

#014    <description>An example that demonstrates simple IoC features.</description>

#015    </objects>

#016    </spring>

#017    </configuration>

 

将在应用程序示例中用到的对象配置成嵌套在<objects/>元素中的<object/>元素。

 

(2)获得IApplicationContext应用。代码如下所示:

 

#001    using System;

#002    using System.Configuration;

#003    using Spring.Context;

#004    ...

#005    public static void Main ()

#006    { IApplicationContext ctx = ContextRegistry.GetContext();}

#007    ...

 

如上所述,using System.Configuratoin和using Spring.Context两条using语句被加到了MovieApp类文件中。其中,System.Configuration命名空间可以让应用程序存取保存在配置文件中的Spring.NET的IoC容器的定义;Spring.Context命名空间可以让应用程序存取IApplicationContext类,这个类是应用程序存取Spring.NET所提供的功能的主要方法。

方法main获得一个IApplicationContext的实现,它的配置信息已经被写在应用程序配置文件内名为<objects/>的节中。

 

(3)第一个对象的定义。到目前为止,在应用程序配置文件中还没有对象定义,所以下面定义一个对象。MovieLister实例的XML定义如下所示。

 

#001    ...

#002    <objects>

#003      <object name="MyMovieLister"

#004             type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">

#005      </object>

#006    </object>

#007    ...

 

#003行:MyMovieLister是该对象的唯一标识符,使用这个标识符,这个对象的实例就能够被如下所示的代码中的IApplicationContext引用所获取。

 

#001    ...

#002    public static void Main ()

#003      { IApplicationContext ctx = ContextRegistry.GetContext();

#004        MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");}

#005    ...

 

lister实例仍然没有与IMovieFinder接口相应的实现注入。此时如果使用MoviesDirectedBy方法会导致NullReferenceException异常发生,因为lister实例仍然还没有对IMovieFinder的引用。注入到lister实例中的IMovieFinder接口实现的XML配置定义如下。

 

#001    ...

#002       <objects>

#003          ...

#004          <object name="MyMovieFinder"

#005         type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>

#006          </object>

#007          ...

#008       </object>

#009    ...

 

(4)设值注入。下面要做的是把以MyMovieFinder为标识符的IMovieFinder实例注入到以MyMovieLister为标识符的MovieLister实例中。这个注入动作使用设值注入的方法,代码如下所示:

 

#001    ...

#002    <objects>

#003    <object name="MyMovieLister"

#004    type="Spring.Examples.MovieFinder.MovieLister, Spring.Examples.MovieFinder">

#005    <!-- using setter injection... -->

#006    <property name="movieFinder" ref="MyMovieFinder"/>

#007    </object>

#008    <object name="MyMovieFinder"

#009    type="Spring.Examples.MovieFinder.SimpleMovieFinder, Spring.Examples.MovieFinder"/>

#010    </object>

#011    </objects>

#012    ...

 

当IApplicationContext接口取得了MyMovieLister对象以后,Spring.NET的IoC容器将会把对MyMovieLister对象的引用注入到MyMovieLister对象的MovieFinder属性中去。这样,在程序中引用的MovieFinder对象即配置完毕,可以列出某导演导过的所有电影。

 

#001    ...

#002    public static void Main ()

#003    {

#004    IApplicationContext ctx = ContextRegistry.GetContext();

#005    MovieLister lister = (MovieLister) ctx.GetObject ("MyMovieLister");

#006    Movie[] movies = lister.MoviesDirectedBy("Roberto Benigni");

#007    Console.WriteLine ("/nSearching for movie.../n");

#008    foreach (Movie movie in movies)

#009    {Console.WriteLine (

#010     string.Format ("Movie Title = '{0}', Director = '{1}'.",

#011     movie.Title, movie.Director));}

#012    Console.WriteLine ("/nMovieApp Done./n/n");

#013    }

#014    ...

 

也可以使用构造注入的方法,读者可以自己考虑构造注入方法的实现方式,这里就不再具体介绍。

现在Spring.Net的1.1版本已经发布,读者可以通过访问http://sourceforge.net/ projects/ springnet下载。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值