Spring.Net 框架(一)(二)(三)

Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为基础,将Spring.Java的核心概念与思想移植到了.NET平台上。

企业级应用一般由多个物理层组成,每个物理层也经常划分为若干功能层。不同层次之间需要相互协作,例如,业务服务层一般需要使用数据访问层的对象来实现某个用例。不管应用程序如何构建,最终都会表现为一系列相互协作的对象,这些对象一起组成了完整的应用程序。所以我们说,应用程序中的对象之间相互具有依赖性。

.NET平台为构建应用程序提供了丰富的功能,从非常基础的基元类型和基础类库(以及定义新类的方法),到功能完善的应用程序服务器和Web框架,都有很好的支持。但.NET平台本身并没有提供任何方式来管理基础的应用模块并将它们组合为一个相互协作的整体,只能依靠架构师或开发人员去创建(一系列)应用程序。诚然,目前有很多设计模式可用于业务系统的设计,我们可以使用这些模式将各种类或对象组合成能够正常工作的完整应用。工厂、抽象工厂、 Builder、装饰及服务定位器(Service Locator)等模式已被现今的软件开发行业广泛接受和采用(这也许正是这些模式最早被定型为模式的原因)。这些模式都非常好,但也不过是些已命名的最佳编程方法,在对这些模式的介绍中一般还会说明它们是作什么用的、最好应用到什么场合、可以解决什么问题等等。我们可以从许多书籍和wiki上找到这些模式,然后仔细研读,然后实现在我们自己的应用中。

Spring.NET的IoC容器所解决的,正是如何在企业应用中将类、对象和服务组合成应用程序的问题。IoC容器通过很正统(按: formalized,言下之意是这些方式都是已经定型了的、经过了业界多年考验的)的方式将分散的组件组合成完整的应用程序。Spring.NET框架所采用的,都是被业界无数应用程序考验多年的、已经被定型为设计模式的最佳编程方式,实际上,这些模式已经成为我们架构和开发时的法典,而通过 Spring.NET,我们可以直接将它们整合到自己的应用程序中。目前已有很多组织和机构用Spring框架开发出了强壮的、维护性好的应用程序,这确实是一件非常好的事情。

 

Spring.NET框架包括很多功能,这些功能被很好的组织进一系列模块当中,如下图所示。Spring.NET 1.0包括完整的IoC容器和AOP类库。1.1版将加入Web、ORM和数据模块。Spring.NET的下载包中并不包含与其它类库(如 NHibernate,TIBCO EMS,Anthem,和IIOP.NET)集成的模块,如果需要您可以单独下载。下图为Spring.NET的各个核心模块。灰色阴影部分在1.0版中已经可用了,其它模块会在未来版本中陆续发布。目前可以从我们的网站上单独下载这些新的模块。


Spring.Core作为整个框架的基础,实现了依赖注入的功能。Spring.NET的大部分模块都要依赖或扩展该模块。 Spring.Core的基础是IObjectFactory接口,该接口用一个简单而优雅的方式实现了工厂模式,使我们可以无需自行编写 singleton类型和众多的服务定位器,并允许将对象配置及其依赖关系与具体的程序逻辑解耦。该模块中的IApplicationContext接口是IObjectFactory的扩展,增加了诸多企业级功能,包括使用资源文件进行文本本地化、事件传播和资源装载等等。

Spring.AOP为业务对象提供面向方面编程(AOP)的支持。AOP完善了IoC容器的功能,为创建企业应用和使用声明式服务奠定了坚实的基础。

Spring.Web对ASP.NET进行了一系列功能扩展,包括对ASP.NET页面进行依赖注入、双向数据绑定、在ASP.NET 1.1中使用Master page、以及增强的本地化功能支持等。

Spring.Services允许将任意的“普通”对象(意为没有继承任何指定基类型的对象)发布为企业服务(COM+)或远程对象。通过依赖注入和特性元数据覆盖等功能,该模块可使.NET的Web服务获得极大的灵活性。同时也支持Windows后台服务。

Spring.Data定义了一个抽象的数据访问层,可以跨越各种数据访问技术(从ADO.NET到各种ORM)进行数据访问。该模块包含一个ADO.NET的抽象层,减少了使用传统ADO.NET进行编码和事务管理时的工作量。

Spring.ORM为时下流行的ORM类库提供了一个整合层,其中包含声明式事务管理等诸多功能。

IObjectFactory是初始化、配置及管理对象的实际容器(按:它是所有容器的父接口)。对象间通常会相互协作,我们也可以说它们相互间具有依赖性。这些依赖性通过IObjectFactory的配置数据反映出来。(但某些依赖性从配置数据中是看不到的,比如运行时对象之间的方法调用。)

Spring.Objects.Factory.IObjectFactory接口有多个实现类。最常用的是 Spring.Objects.Factory.Xml.XmlObjectFactory。关于如何在代码中与IObjectFactory交互,与IObjectFactory交互。IApplicationContext接口所定义的增强功能将在IApplicationContext简介中讨论。

前文提到过,Spring.NET框架的核心原则是非侵入性。简单的说,就是应用程序的代码不需要对Spring.NET的API有任何依赖。然而,如果要通过IObjectFactory或Spring.Context.IApplicationContext接口充分利用IoC容器的功能,有时候还必须要初始化这两个接口的某个实现类。为此,可以在代码中使用new操作符来显式创建容器(在C#中,VB开发人员则使用New操作符。按:后文中,举凡涉及到含义为“管理对象的容器”而非特指接口的名称时,将原文中的IObjectFactory或IApplicationContext称为 “容器”或“IoC容器”);另一种更为简单的方式是在.NET应用程序的标准配置文件中用一个自定义节点来配置容器。一旦容器建立,应用程序代码就可能不再需要与之发生显式的交互了。

下面代码创建了XmlObjectFactory类的一个实例,XmlObjectFactory是IObjectFactory的实现类之一。我们假定在objects.xml文件中定义了要装配(按:装配的概念见后文)和发布的服务对象。将该文件的信息传递给XmlObjectFactory的构造器,即可创建一个容器,参见如下代码:

 
  
IResource input =  new FileSystemResource ("objects.xml"); IObjectFactory factory =  new XmlObjectFactory(input);

代码中使用了Spring.NET的IResource接口。IResource能以简单统一的方式访问许多可用System.IO.Stream表示的IO资源。 IResource接口中将对IResource接口展开讨论。这些IO资源一般是独立的文件或者URL,但也可以是.NET程序集的内嵌资源。通过IResource接口,可以用简单的URI格式来描述资源的位置,比如可用file://object.xml来表示一个文件。此外,IResource也支持很多其它协议,如 http等。

前文提到IApplicationContext是IObjectFactory的超集,我们一般都会用IApplicationContext来作为容器。在创建容器时可以像上例一样用IResource实例化IApplicationContext接口的任何一个实现类。另外, IApplicationContext支持用多个配置文件创建容器(按:此处的配置文件是指包括了Spring.NET对象定义的XML文件,而非特指.config文件,下同):
IApplicationContext context =  new XmlApplicationContext(
    "file://objects.xml",
    "assembly://MyAssembly/MyProject/objects-dal-layer.xml");

//  of course, an IApplicationContext is also an IObjectFactory...
IObjectFactory factory = (IObjectFactory) context;
 

下面是引用.NET程序集内嵌资源时的URI语法:assembly:////
注意

若要在VS中创建一个内嵌的资源,必须在文件属性编辑器中将xml文件的Build Action设为Embedded Resource。并且,如果自上次成功建立项目之后,该属性的变更是本次所做的唯一更改,则需要显式的重新生成项目。如果使用NAnt建立项目,需要在 csc任务中添加一个节点。可参见Spring.Core.Tests项目中的build文件,该项目随 Spring.NET一起发布。

更好的创建方式是在标准.NET应用程序配置文件中(App.config或Web.config)添加自定义配置节点。以下的XML节点可以创建与前例相同的容器:

 
  
< spring >    < context  type ="Spring.Context.Support.XmlApplicationContext, Spring.Core" >      < resource  uri ="file://objects.xml" />      < resource  uri ="assembly://MyAssembly/MyProject/objects-dal-layer.xml" />    context > spring >

节点的type属性是可选的,在Windows应用中,其默认值就是Spring.Context.Support.XmlApplicationContext,所以下面的配置和上面完全相同:

 
  
< spring >    < context >      < resource  uri ="file://objects.xml" />      < resource  uri ="assembly://MyAssembly/MyProject/objects-dal-layer.xml" />    context > spring > 

spring和context节点的名称不是任意的,必须是"spring"和"context",Spring.NET本身将 "spring/context"作为字符串常量定义在了AbstractApplicationContext类中以表示上下文的节点名称。若要引用由以上配置创建的容器,可使用下面的代码:

 
  
IApplicationContext ctx = ContextRegistry.GetContext();

ContextRegistry类既可用来初始化应用程序上下文,也可用来以服务定位器风格对容器中的对象进行访问。注意,使这一切成为可能的是Spring.Context.Support.ContextHandler类,该类实现了FCL的IConfigurationSectionHandler接口。必须在.NET配置文件的 节点中注册这个类,如下所示:

 
  
< configSections >    < sectionGroup  name ="spring" >      < section  name ="context"  type ="Spring.Context.Support.ContextHandler, Spring.Core" />    sectionGroup > configSections >

注册了这个节点处理器后,配置文件中的节点才能起作用。

在某些情况下,用户不需要以任何方式显式创建容器,Spring.NET可以自行创建。例如,Spring.NET中的Spring.Web模块可以将 IApplicationContext作为Web应用程序正常启动进程的一部分进行自动装载。目前正在研究如何为WinForms应用程序提供类似的支持
关于如何通过编程方式管理IObjectFactory,稍后再讲。下面我们先讨论IObjectFactory接口所使用的对象配置格式。(按:凡适用于IObjectFactory的内容,必定也适用于IApplicationContext)

基本上,IObjectFactory的配置信息由一个或多个对象定义构成。在基于XML的工厂中,这些对象定义表现为一个或多个< object>子节点,它们的父节点必须是(按:objects节点的xmlns元素是必需的,必须根据不同的应用添加不同的命名空间,请留意各个章节中的相关内容。

 
  
< objects  xmlns ="http://www.springframework.net"       xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation ="http://www.springframework.net         http://www.springframework.net/xsd/spring-objects.xsd" >    < object  id ="..."  type ="..." >   ...    object >    < object  id ="...."  type ="..." >   ...    object >   ... objects >

随Spring.NET一起发布的schema文档可以简化对XML对象定义的验证过程。该文档是完全开放的(参见附录A,Spring.NET的spring-objects.xsd)。目前,除了验证XML文档外,该文档还有一个用途,就是在具备XSD感知能力的编辑器(比如VS.NET)内进行代码提示。可参考第二十四章,与Visual Studio.NET集成。在Spring.NET的网站上可以下载到spring-objects.xsd的最新版本。

XML对象定义也可以放在.NET的标准应用程序配置文件中。此时也需要为节点预先注册相应的节点处理器,类型为 Spring.Context.Support.DefaultSectionHandler。然后,就可以在.NET的.config文件中为一或多个容器配置对象定义了,如下所示:

 
  
< 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" >         ...      objects >    spring > configuration >

上下文嵌套和从其它文件中导入对象定义会讨论创建配置文件时可用的其它节点。

我们也可以在配置文件中为IApplicationContext注册自定义的资源处理器、schema解析器、类型转换器和类型别名等等。IApplicationContext配置一节中将讨论这些内容。

容器所管理的对象由对象定义来配置,一个对象定义包含以下信息:

  • 对象类型,即所定义对象的实际类型。
  • 对象行为,用来规定对象在IoC容器中的行为(例如,是否布署为singleton,自动装配的模式,依赖检查的模式,初始化和销毁方法等)。
  • 对象创建后要设置的属性值。例如,一个线程池管理对象的可用线程数,或者用来创建线程池的类型信息,* 对象所需要的其它对象,例如一个对象的协作对象(同样可通过属性或构造器设置)。这些对象也可以叫做依赖项。

上面提到了用属性和构造器参数来设置依赖项。Spring.NET支持两种类型的IoC:类型2和类型3(分别是构造器参数注入和属性注入)。也就是说,当一个对象被IoC容器创建时,既可以使用常规的属性设值方法为属性设值,也可以直接向构造器传递参数来为属性赋值。(按:对.NET来说,“属性注入” 似乎比“设值方法注入”更贴切)

上述概念直接对应对象定义中的一系列xml子节点,这些节点及相关的章节如下表所示:

 对象定义内容
内容 详细信息
对象类型 对象的创建
id和name 对象标识符(id和name)
singleton或prototype Singleton和Prototype
对象属性 设置对象的属性和协作对象
构造器参数 设置对象的属性和协作对象
自动装配模式 自动装配协作对象
依赖检查模式 检查依赖项
初始化方法 生命周期接口
销毁(destruction)方法 生命周期接口

 

对象定义会包含对象的类型信息(也有例外,通过实例工厂方法创建对象和,抽象及子对象定义)。多数情况下,容器会根据对象定义中的type属性值去直接调用相应类型的某个构造器。另外,容器也可以调用工厂方法来创建对象,这时type属性的值就应该是包含工厂方法的类型(按:而不是要创建的类型,但通过该对象定义的名称获取的则是由工厂方法所创建的对象)。工厂方法的产品对象可以是工厂方法所在的类型,也可以是其它类型(按:很多情况下工厂方法位于单独的类型中),这无关紧要。
 

使用构造器创建对象时,并不要求对象必须是某种特定的类型,也不需要了解它的实现方式(按:也就是说,类型不必去实现某个接口或扩展某个基类以求和 Spring.NET兼容,任何对象都可以布署在容器中)。只要指明对象类型(以及它所在的程序集名称)就可以了。不过,根据不同IoC容器的要求,可能需要为类型(显式的)定义默认构造器(即无参的构造器)。(按:由于.NET只会为没有构造器的类型自动添加默认构造器,所以Spring.NET允许类型不定义任何构造器;但如果在定义了含参构造器后仍需使用无参构造器,则必须进行显式定义。)

XmlObjectFactory类实现了IObjectFactory接口,它可以处理XML文件中的对象定义,例如:

 
  
< object  id ="exampleObject"  type ="Examples.ExampleObject, ExamplesLibrary" />


这个节点定义了一个名为exampleObject的对象,它的类型是位于ExamplesLibrary程序集中的 Examples.ExampleObject类。请特别留心一下type属性的格式:类型的全名,然后是一个逗号,最后是类型所在的程序集名称。在上面的例子中,ExampleObject类定义在Examples命名空间,且位于ExamplesLibrary程序集中。

type属性值必须包含类型所在的程序集名称。另外,如果需要确保Spring.NET能按照预期的类型创建对象,则推荐使用程序集的强命名。不过,一般只有在用到GAC中的程序集时,才需要使用强命名。(按,例如,type="System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b77a5c561934e089")

如果需要为嵌套类型创建对象,可以使用+号。例如,如果在类型Examples.ExampleObject嵌套定义了类型Person,就可以用下面的方式创建对象定义:

< object  id ="exampleObject"  type ="Examples.ExampleObject+Person, ExamplesLibrary" />


如果应用程序能够以标准的程序集探测机制访问程序集(例如ASP.NET中的bin文件夹),那么type属性的值只需包括类型全名即可。这样,当程序集改变后,不需要去修改每个对象定义的type属性(主要是改些版本号等等),Spring.NET就会自动使用最新的程序集。


你可能感兴趣的:(.NET)