编辑21.1. 简介
Spring.Services命名空间的目的是为业务服务提供位置的透明性。我们相信使用普通的接口和.NET类,用户应该可以用最简单的方式实现服务。我们也认为在将某个服务发布给客户端的时,应该只关心服务的配置,而无需关心服务的实现。
在Spring.Services命名空间的支持下,可以用容器中的服务导出对象将任意的普通对象发布为web服务,企业服务组件或远程对象。
我们相信这也是迁移到Indigo最简单的方式。只要在设计时使用了接口并将其实现为普通的.NET类型,那么一旦Indigo发布,我们就应该能实现Indigo的导出类。
目前,使用现有的导出类和Spring.NET配置文件,我们可以将实现了某个接口的任意对象发布为企业服务。
顶部编辑21.2. 服务组件
.NET的服务组件允许我们使用COM+服务提供的功能,比如声明式的分布式事务管理、基于角色的安全性验证、对象池消息等等。使用这些服务的类型必须要继承自System.EnterpriseServices.ServicedComponent类,类型和程序集也需要应用相关的特性,并且需要将服务组件注册到COM+目录以配置应用程序。在.NET中,访问和使用COM+服务的相关内容称为.NET企业服务。
如果使用Spring.NET的远程框架,很多服务类都不需要从ServicedComponent继承。您可能对如何将一个普通的对象发布为服务组件,以及如何使客户端以位置透明的方式访问服务组件很感兴趣。使用Spring.NET的ServicedComponentExporter、 EnterpriseServicesExporter和ServicedComponentfactory类,就可以很方便的完成这些工作,服务对象不需要继承ServicedComponent类,并且为程序集进行强命名和使用RegSvcs.exe工具这样的手工工作也可以自动完成。
注意本章的内容并没有对.NET企业服务做深入的讨论,有兴趣的话可以参考Christian Nagel的著作《Enterprise Services with the .NET Framework》。
顶部编辑21.3. 服务端
在服务端导出服务组件时,最大的问题是组件必须要位于文件系统的某个物理程序集内,这样才能将之与COM+服务注册。同时,要想成功注册,程序集必须使用强命名,这使注册过程变得更加复杂了。
Spring.NET提供了两个类来处理这些工作:
- Spring.Enterprise.ServicedComponentExporter负责将一个类导出为服务组件,并确保它继承了ServicedComponent。同时也允许为组件应用类型级和方法级的特性,以便定义事务行为和消息队列。
- Spring.Enterprise.EnterpriseServicesExporter 则相当于一个COM+应用程序,可以用它来指定要在应用程序中发布的组件列表(按:即由ServicedComponentExporter导出的服务,见下文例子)、应用程序名称和其它程序集级的特性。
现在,假如我们有一个简单的服务接口和一个实现类,如下:
namespace MyApp.Services
{
public interface IUserManager
{
User GetUser(int userId);
void SaveUser(User user);
}
public class SimpleUserManager : IUserManager
{
private IUserDao userDao;
public IUserDao UserDao
{
get { return userDao; }
set { userDao = value; }
}
public User GetUser(int userId)
{
return UserDao.FindUser(userId);
}
public void SaveUser(User user)
{
if (user.IsValid)
{
UserDao.SaveUser(user);
}
}
}
}
在配置文件中,相应的对象定义如下:
<object id="userManager" type="MyApp.Services.SimpleUserManager">
<property name="UserDao" ref="userDao"/>
</object>
假设我们需要将userManager发布为一个服务组件以便使用事务功能。首先需要用ServicedComponentExporter导出我们的服务,如下:
<object id="MyApp.EnterpriseServices.UserManager" type="Spring.Enterprise.ServicedComponentExporter, Spring.Services">
<property name="TargetName" value="userManager"/>
<property name="TypeAttributes">
<list>
<object type="System.EnterpriseServices.TransactionAttribute, System.EnterpriseServices"/>
</list>
</property>
<property name="MemberAttributes">
<dictionary>
<entry key="*">
<list>
<object type="System.EnterpriseServices.AutoCompleteAttribute, System.EnterpriseServices"/>
</list>
</entry>
</dictionary>
</property>
</object>
ServicedComponentExporter对象会用组合方式为SimpleUserManager对象创建一个代理对象,代理对象继承了 ServicedComponent,并且代理SimpleUserManager对象上的方法调用。同时,导出类会为代理对象应用 TransactionAtribute特性、并为其中的所有方法应用AutoCompleteAttribute特性。
下面,需要为组件的宿主COM+程序配置一个导出类对象:
<object id="MyComponentExporter" type="Spring.Enterprise.EnterpriseServicesExporter, Spring.Services">
<property name="ApplicationName" value="My COM+ Application"/>
<property name="Description" value="My enterprise services application."/>
<property name="AccessControl">
<object type="System.EnterpriseServices.ApplicationAccessControlAttribute, System.EnterpriseServices">
<property name="AccessChecksLevel" value="ApplicationComponent"/>
</object>
</property>
<property name="Roles">
<list>
<value>Admin : Administrator role</value>
<value>User : User role</value>
<value>Manager : Administrator role</value>
</list>
</property>
<property name="Components">
<list>
<ref object="MyApp.EnterpriseServices.UserManager"/>
</list>
</property>
<property name="Assembly" value="MyComPlusApp"/>
</object>
这个导出类对象会将Components属性中列出的所有代理类放入以Assembly属性值为名的程序集中,为其设置强命名,并注册为以 ApplicationName属性值为名的COM+服务应用。如果指定的应用尚未存在,就会用配置中的Description、 AccessControl和Roles属性值新建并配置该应用程序。
顶部编辑21.4. 客户端
由于服务组件类是动态生成与注册的,所以客户端不能在代码中使用new操作符来初始化服务对象,而要通过 Spring.Enterprise.ServicedComponentFactory获取。通过指定组件的配置模板或远程服务组件的名称,就可以用该类获得服务对象,如下:
<object id="enterpriseUserManager" type="Spring.Enterprise.ServicedComponentFactory, Spring.Services">
<property name="Name" value="MyApp.EnterpriseServices.UserManager"/>
<property name="Template" value="userManager"/>
</object>
随后,在IoC容器中就可以将这个对象注入到任何需要使用IUserManager接口的对象中,就和使用普通的 SimpleUserManager对象没有区别。我们在例子中也看到,使用实现了某个接口的普通.NET类来创建服务,我们确实可以通过配置使服务做到位置透明性。
顶部