一.具体介绍
目的:降低业务逻辑层耦合和进行动态加载提高系统的可扩展性
使用工具: spring.net框架 通过IOC注入机制实现
二.具体机制
面向接口编程,所有的服务都需要提供对外的接口,通过spring.net的IOC容器进行服务的 组合,通过依赖注入形式将需要的服务进行注入。
新增模块规范描述如下:
当系统需要添加新服务—>首先声明该服务接口—>实现该服务接口—>将此实例注入到spring.net IOC容器—>将此服务放入ServiceContainer统一进行管理—>通过模块容器(服务容器)来对服务进行调用,也可以通过构造或者set注入要使用的服务。
案例一(基本流程)
1.对所有的服务都要提出对外的接口 如我们要实现一个查询用户服务,需要首先进行接口声明:
public interface IUserManageService { /// <summary> /// 返回用户的详细信息的方法 /// </summary> /// <returns></returns> string GetUserInfo(); } |
2.实现以上接口
class UserManageSerciceImpl : IUserManageService { public UserManageSerciceImpl() { } public string GetUserInfo() { User user = new User(); user.Oid = 1; user.Name = "Beniao"; user.Sex = "Boy"; user.Age = 25; return string.Format("编号:{0}--->姓名:{1}--->性别:{2}--->年龄:{3}", user.Oid, user.Name, user.Sex, user.Age); } } |
3.将该服务配置到IOC容器中:(独立配置文件如UserManagerService_spring.xml)
<objectname="UserManageService" type="TestSprintNet.ServiceImpl.UserManageSerciceImpl, TestSprintNet" /> |
4. 在整个服务的app.config文件中加入UserManagerService_spring.xml,通过app.config加入UserManagerService服务。?????????
<spring> |
5.服务容器定义:
要求:单例模式
*将IUsersManagerService接口交给ServiceContainer管理[对于这种模块管理以单例模式进行设计]
public class ServiceContainer { private ServiceContainer Instance; private IUsersManagerService userService; public IApplicationContext context; private static ServiceContainer serviceContainer=null; private ServiceContainer()(单例) { } public static ServiceContainer Instance { Get { if (serviceContainer == null) { serviceContainer = new ServiceContainer(); context = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; } return serviceContainer; } public IUsersManagerService UserService { get { | |
userService = context.GetObject("UserServiceImp") as IUsersManagerService; //通过IOC注入在这里得到UserService实例对象
return userService; } } } |
|
说明: 关于“Spring/Context”的含义:
首先,我们要添加Spring.Contet命名空间的引用,以便用IApplication接口访问IoC容器。然后解析配置文件的“Spring/Context”节点(即应用程序配置文件中一个Spring节点下的Context子节点)得到一个IApplicationContext对象,接下来的事情就是向该Context索要相关的对象
(IUsermanageService),context.GetObject("UserManageService"),其中“UserManageService”由配置文件指定生成方式和注入方式。
6.使用服务容器
public class UsersManager { private UsersMangerService usersManagerService=ServiceContainer.Instance.IUsersManagerService;(在这里通过ServiceContainer直接可以调用IUsersManagerService服务,当然这个服务是在spring.net里注???????、??入过的) //具体使用…………………….. }
|
案例二(组合服务)
1. 现在我们要增加一个服务模块ICompanyManageService代码如下:
其中在此服务中我们需要UsersMangerService服务,下面将介绍怎样将UsersMangerService服务与ICompanyManageSercice服务进行组合。
namespace TestSprintNet.ServiceInterface { public interface ICompanyManageSercice { string getAllUser(); } } |
2.上述接口的实现类
class CompanyManageServiceImpl : ICompanyManageSercice { private IUserManageService _userManage;
private IUserManageService _userManageService;
//构造器注入(注入IUserManageService服务) public CompanyManageServiceImpl(IUserManageService userManageService) { |
|
this._userManageService = userManageService; } public string getAllUser() { //return _userManage.GetUserInfo(); return _userManageService.GetUserInfo(); } } |
}
3.将该服务配置到IOC容器中并且将案例一中的IUserManageService服务注入到其中:
<object name="CompanyManageService" type="TestSprintNet.ServiceImpl.CompanyManageServiceImpl, TestSprintNet"> //set注入组合方式: <property name="userManageService" ref="UserManageService"/>??????? //构造子注入组合方式: <constructor-arg name="userManageService" ref="UserManageService"/> </object> |
4.通过spring框架对实例进行调用:
ICompanyManageSercice companyManageService = context.GetObject("CompanyManageService") as ICompanyManageSercice; Console.WriteLine(companyManageService.getAllUser()); |
在此案例中我们将UserManageService服务给CompanyManageSercice提供服务支持这样就完成了服务的组合。
三:实现依赖注入的方式
1.设值注入:
通过setter方法设定依赖关系显得更加直观,更加自然。
如果依赖关系(或继承关系)较为复杂, 设值注入往往更为简洁。
<object name="CompanyManageService" type="TestSprintNet.ServiceImpl.CompanyManageServiceImpl, TestSprintNet"> <property name="UserManage" ref="UserManageService"/> </object> |
2构造子注入:
(1)避免了繁琐的setter方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,
(2)由于没有setter方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处于
相对“不变”的稳定状态,无需担心上层代码在调用过程中执行setter方法对组件依赖关系
产生破坏,特别是对于Singleton模式的组件而言,这可能对整个系统产生重大的影响。
(3)同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。
对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的
层次清晰性提供了保证。
(4)通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量
依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要。
<object name="CompanyManageService" type="TestSprintNet.ServiceImpl.CompanyManageServiceImpl, TestSprintNet"> <constructor-arg name="UserManageService" ref="UserManageService"/> </object> |
四.进行单元测试
项目开发的过程中,程序需要单元测试,在spring.net的机制中,可以自行完成一个测试类。
namespace TestSprintNet { static class Program { static void Main() { try { IApplicationContext context = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext; ICompanyManageSercice companyManageService = context.GetObject("CompanyManageService") as ICompanyManageSercice; Console.WriteLine(companyManageService.getAllUser()); Console.Read();
} catch(Exception e) { } } } } |
然在进行测试之前,必须完成对CompanyManageService的依赖注入。
五.对于配置文件的定义标准:
不同服务采用不同的文件进行配置,然后在一个总的配置文件中提供对这些服务配置文件的引用。这样方便对单个服务进行测试。步骤如下:
(1)针对服务类添加一个单独配置文件UserManageService.xml
<?xml version="1.0" encoding="utf-8" ?> <objects> <!--您的对象定义 --> <object name="UserManageService" type="TestSprintNet.ServiceImpl.UserManageSerciceImpl, TestSprintNet" /> </configuration> </objects> (2)把(1)单独配置文件写入总的配置文件 在App.Config中添加 <spring> <context> |
<resource uri="config://spring/objects"/> <resource uri="assembly://(项目名称)/UserManageService.xml "/> </context> <objects xmlns="http://www.springframework.net"/> </spring> |
六.利用O/R mapping机制完成调用UserService服务:
1. 建立数据表 (在Test数据库中)
数据库为SQLServer2000,表名为:users
字段名称 | 类型 | 长度 |
LogonId(主键) | varchar | 50 |
UserName | varchar | 50 |
Password | varchar | 50 |
EmailAddress | varchar | 50 |
2. 建立对象
实体对象名称为Users
namespace Sample.NHibernate.NHibernateTest
public string EmailAddress }
|
3. 建立XML对像映射文件
文件名称Users.hbm.xml(此文件应该和Users.cs文件放在同一个目录下)
<?xml version="1.0" encoding="utf-8" ?> |
注意事项:在Visual Studio 2003/2005中要将此文件的属性设置为“嵌入的资源”(Embedded Resource),SpringNHibernateSample是这个项目的名称,
此时,完成了数据表和实体类之间的映射。
4. 在Spring.NET的容器中添加Nhibernate的配置(Spring_nhibernate.xml)
<?xml version="1.0" encoding="utf-8" ?> <object id="SessionFactory" <property name="DbProvider" ref="DbProvider"/> |
<entry
key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
<entry key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect"/>
<entry key="hibernate.connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/>
</dictionary>
</property>
</object>
<object id="HibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate">
<property name="DbProvider" ref="DbProvider"/>
<property name="sessionFactory" ref="SessionFactory"/>
</object>
<object id="TransactionInterceptor"
type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data">
<property name="TransactionManager" ref="HibernateTransactionManager"/>
<property name="TransactionAttributeSource">
<object type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data"/>
</property>
</object>
<!-- 以下是业务相关的 -->
<object id="UsersManagerService"
type="Sample.NHibernate.NHibernateTest.UsersManagerService, SpringNHibernateSample">
<property name="SessionFactory" ref="SessionFactory"/>
</object>
</objects>
需要解释的是:对于每一个大的模块我们都要建立起Spring_nhiberate.xml这样的文件所以为了我们的管理最好是将这种spring.net的配置文件放在项目根目录的一个文件夹里,然后将spring_nhibernate.xml这种文件交给app.config文件去管理
5.对Spring.NET的容器进行初始化(app.config)
app.config文件是在项目启动的同时就进行加载的,所以当我们把Spring_nhibernate.xml文件放在此文件中进行管理时,在项目启动的时候Spring_nhibernate.xml也就被装载了
<spring> |
6.添加一个人员管理服务(完整的过程)
创建一个服务接口IUsersManagerService:
| using System; using System.Collections; | |
using Spring.Data.NHibernate.Support; namespace Sample.NHibernate.NHibernateTest.Interface; interface IUsersManagerService { public bool SaveObject(Users user); public bool DeleteObject(Users user); public bool UpdateObject(Users user); public IList GetAllObjectsList(); public User Load(Object ID); |
| |
}
*将IUsersManagerService接口交给ServiceContainer管理[对于这种模块管理我们需要将以单例模式进行设计]
public class ServiceContainer
{
private ServiceContainer Instance;
private IUsersManagerService userService;
public IApplicationContext context;
private static ServiceContainer serviceContainer=null;
private ServiceContainer()
{
}
public static ServiceContainer Instance
{
get{
if (serviceContainer == null)
{
serviceContainer = new ServiceContainer();
context = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext;
}
return serviceContainer;
}
}
public IUsersManagerService UserService
{
get
{
userService = context.GetObject("UserServiceImp") as IUsersManagerService;
return userService;
}
}
}
实现IUsersManagerService接口并且继承HibernateDaoSupport,对于HibernateDaoSupport类,是一个spring.net框架提供的类,加入spring.net支持后,便可以继承HiberanteDaoSupport这个类。
using System;
using System.Collections;
using Spring.Data.NHibernate.Support;
namespace Sample.NHibernate.NHibernateTest
{
public class UsersManagerService : HibernateDaoSupport,IUsersManagerService?????
{
public bool SaveObject(Users user)
{
HibernateTemplate.Save(user);
return true;
}
public bool DeleteObject(Users user)
{
HibernateTemplate.Delete(user);
return true;
}
public bool UpdateObject(Users user)
{
HibernateTemplate.Update(user);
return true;
}
public IList GetAllObjectsList()
{
return HibernateTemplate.LoadAll(typeof(Users));
}
public User Load(Object ID)
{
return (User)HibernateTemplate.Load(typeof(Users),ID);
}
}
}
7.将UsersManagerService服务添加到具体模块中使用(UsersManager.cs)
using System;
namespace Sample.NHibernate.NHibernateTest
{
public class UsersManager
{
private ServiceContainer.Instance.IUsersManagerService; //在这里通过ServiceContainer直接可以调用UserService服务,当然这个服务是在spring.net里注入过的
public void insertUser
{
Users newUser = null;
try
{
newUser = usersManagerService .load("joe_cool");
}
catch
{}
if(newUser==null)
{
newUser = new Users();
newUser.Id = "joe_cool";
newUser.UserName = "Joseph Cool";
newUser.Password = "abc123";
newUser.EmailAddress = "joe@cool.com";
usersManagerService.SaveObject(newUser);
}
}
}
至此,利用ormapping操作数据完成
(以下还在完善中)
七.如何通过Spring.Net定义Web Service以及在客户端调用Web Service
1.消除对.asmx的依赖
namespace MyComany.MyApp.Services { [WebService(Namespace = "http://myCompany/services")] public class HelloWorldService {
[WebMethod] public string HelloWorld() { return "Hello World!"; } } |
}
这是一个普通的类型,其中应用了两个.NET特性:方法级的[WebMethod]及类型级的[WebService]。开发人员可以在VS.NET中创建这个服务类。
要想将这个对象发布为web服务,只需依次作以下操作:
1.在web.config文件中,将Spring.Web.Services.WebServiceFactoryHandler注册为响应*.asmx请求的HTTP Handler。
<system.web> <httpHandlers> <add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/> </httpHandlers> </system.web>
|
当然,也可以为*.asmx请求注册其它的Http Handler来处理意外情况,不过一般没这个必要,因为如果
WebServiceFactoryHandler无法在容器中找到与某个请求相匹配的对象定义,就会求助于.NET标准的Http
Handler去查找.asmx文件。
2.为web服务创建XML对象定义
<object name="HelloWorld.asmx" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/> |
注意,为web服务创建的对象定义并不要求一定是抽象的(通过设置abstract="true"),但设为抽象可防止创建不必要的服务实例,因为.NET基础框架会在后台自动为每次请求创建新的服务实例,Spring.NET需要做的只是提供服务的类型名,即便标记为抽象,也能从容器中获取服务对象的实例。所以Spring.NET建议将Web服务的定义显示标记为抽象。
现在就能以对象定义中name属性的值为服务名来访问这个Web服务了:
http://localhost/MyWebApp/HelloWorld.asmx
另一种:
1. 新建一个webservice 项目,名为“WebserviceTest”。
2. 新建一个webservice 类,名为“Hello” ,并添加一个“HelloWorld”方法,代码如下:
[WebService(Namespace="http://www.websharp.org/webservices/")] public class Hello : System.Web.Services.WebService { [WebMethod] public string HelloWorld() { return "Hello World"; } } |
3. 使用上面我们创建的“EnterpriseClient”项目,添加一个接口“IHello” ,代码如下:
public interface IHello { string HelloWorld(); } |
4. 填写配置文件
<Service name="HelloWorldWebService" service-type="WebService" deploy-model="SSingleton" url="http://localhost/webservicetest/hello.asmx" namespace="http://www.websharp.org/webservices/" /> |
5. 在Main方法中添加下面的代码:
public static void Main(string[] args) { IHello hello1= ServiceLocator.FindService ("HelloWorldWebService",typeof(IHello)) as IHello; Console.WriteLine(hello1.HelloWorld()); Console.ReadLine(); } |
6. 运行程序,能够得到下面的结果:
八.业务规则层的优势:
业务规则层的设计的最根本的原因是:希望对繁杂的服务和对象进行高效且有条理的管理,通过IOC机制的灵活性,将系统的扩展性和低耦合性实现到较为理想的状态,清楚地规划出项目本身的逻辑层次,在业务规则层规定的实现机制下,提高项目的弹性.
规则业务层中所有的服务都是要实现对应接口,面向接口编程是此层次的主导思想,也是运用spring.net所要实现的最终目标,而对于项目的扩展性主要体现在以下的几个方面:
(1) 如何实现新增模块
新增模块的增加首先需要声明一个属于这个模块的接口,此后要实现一个这个接口实体类,主要完成对这项功能的具体操作.在这里重要的是要将这个接口利用IOC机制注入到IOC容器中,让spring.net框架对其进行管理.由于IOC的注入,我们可以为此接口配置不同的模块解决方案,项目的低耦合性在于,当我们需要对模块进行变更我们可以完全不去对已经实现好的模块进行内部代码的修改,我们要做的只是在spring.net xml文件中将为此接口声明配置的模块更换为一个设计好的模块就可以了,这样不但大大简化了开发工作的工作量和产生bug的几率,同时也可以很优雅的保持了项目中的逻辑架构,同时我们可以扩展出属于此接口下规范下的模块,实现项目的良好的扩展性.
(2) 如何实现新增服务
对于一个模块来说,它需要许多的服务对其进行支持,那么如何将服务按照设计思想随意的加入到模块中去呢?我们可以将这些服务通过IOC机制组合到模块中,对于服务本身的添加我们可以按照新增模块的方式进行IOC注入,服务的增加可以任意的根据自己的需求配置到所需要的模块中,如若项目在以后需要为某个模块添加新的服务,也可以在IOC容器中进行扩充,然后再相应的实现类的构造方法中进行扩展就可以.同样不会影响到项目中的其他部分,实现了良好的低耦合度和扩展性.
(3) 如何变更服务
对于某些模块中的服务变更我们可以通过IOC机制对服务进行动态变更,随着项目的不同阶段,对项目的各种变化,都可以较为容易的进行服务的拼装和安插,在这些过程中不会影响到整个项目的架构.
(4) 数据库服务
数据库服务和其他的服务的实现方式相同,她它是所有服务中复用率最高的服务,它的使用机制和其他服务或许并不太一样,它的主要职责是为其他服务提供服务,是将此服务通过IOC注入到其他服务中去,对其他服务进行辅助,它会用到spring.net的实体映射机制,对数据库进行映射,同样的我们可以为了以后的某些需要来实现出这个服务的不同版本,也同样可以通过IOC机制对这个服务进行变更,不会影响到项目的整体结构。