一.具体介绍
目的:降低业务逻辑层耦合和进行动态加载提高系统的可扩展性
使用工具: 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 )
< object name = "UserManageService"type="TestSprintNet.ServiceImpl.UserManageSerciceImpl, TestSprintNet"/> |
4. 在整个服务的 app.config 文件中加入 UserManagerService_spring.xml ,通过 app.config 加入 UserManagerService 服务。?????????
< spring > < context > < resource uri ="config://spring/objects"/> < resource uri ="assembly:// 项目名称/文件路径 UserManagerService_spring.xml "/> </ context > < objects xmlns ="http://www.springframework.net"/> </ 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 UsersMangerServiceusersManagerService=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 方法设定依赖关系显得更加直观,更加自然。
如果依赖关系(或继承关系)较为复杂, 设值注入 往往更为简洁。
通过 setter 方法设定依赖关系显得更加直观,更加自然。
如果依赖关系(或继承关系)较为复杂, 设值注入 往往更为简洁。
< object name = "CompanyManageService"type="TestSprintNet.ServiceImpl.CompanyManageServiceImpl, TestSprintNet"> < property name = "UserManage"ref="UserManageService"/> </ object > |
2 构造子注入:
( 1 ) 避免了繁琐的 setter 方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,
( 2 ) 由于没有 setter 方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处于
相对 “ 不变 ” 的稳定状态,无需担心上层代码在调用过程中执行 setter 方法对组件依赖关系
产生破坏,特别是对于 Singleton 模式的组件而言,这可能对整个系统产生重大的影响。
( 3 ) 同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。
对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的
层次清晰性提供了保证。
( 4 ) 通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量
依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要。
( 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
数据库为 SQLServer2000 ,表名为: users
字段名称 | 类型 | 长度 |
LogonId( 主键 ) | varchar | 50 |
UserName | varchar | 50 |
Password | varchar | 50 |
EmailAddress | varchar | 50 |
2. 建立对象
实体对象名称为 Users
namespace Sample.NHibernate.NHibernateTest { public class Users { private string id; private string userName; private string password; private string emailAddress; public User() { } public string Id { get { return id; } set { id = value; } } public string UserName { get { return userName; } set { userName = value; } } public string Password { get { return password; } set { password = value; } public string EmailAddress { get { return emailAddress; } set { emailAddress = value; } } } } |
3. 建立 XML 对像映射文件
文件名称 Users.hbm.xml (此文件应该和Users.cs文件放在同一个目录下)
< ?xml version = " 1.0 " encoding = "utf - 8 " ? > < hibernate - mapping xmlns = "urn:nhibernate - mapping - 2.0 " > < class name = " Sample.NHibernate.NHibernateTest.User, SpringNHibernateSample " table = "users" > < id name = "Id" column = "LogonId" type = "String" length = " 50 " > < generator class = "assigned" /> </ id > < property name = "UserName" column = "UserName" type = "String" length = " 40 " /> < property name = "Password" column = "Password" type = "String" length = " 20 " /> < property name = "EmailAddress" column = "EmailAddress" type = "String" length = " 40 " /> </ class > </ hibernate - mapping > |
注意事项:在Visual Studio 2003/2005中要将此文件的属性设置为“嵌入的资源”(Embedded Resource),SpringNHibernateSample是这个项目的名称,
此时,完成了数据表和实体类之间的映射。
4. 在 Spring.NET 的容器中添加 Nhibernate 的配置( Spring_nhibernate.xml )
<?xml version="1.0" encoding="utf-8" ?> <objects xmlns='http://www.springframework.net'> <object id="DbProvider" type="Spring.Data.Common.DbProviderFactoryObject,Spring.Data"> <property name="Provider" value="SqlServer-2.0"/> <property name="ConnectionString" value="server=(local);user id=sa;pwd=;database=Test"/> </object> <object id="SessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate"> <property name="DbProvider" ref="DbProvider"/> <property name="MappingAssemblies"> <list> <value>SpringNhibernateSample</value> </list> </property> <property name="HibernateProperties"> <dictionary> |
<entry
key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
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>
<!-- 以下是业务相关的 -->
<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 > < context > < resource uri ="config://spring/objects"/> < resource uri ="assembly:// 项目名称/Spring_nhibernate.xml文件存放路径/ Spring_nhibernate.xml "/> </ context > < objects xmlns ="http://www.springframework.net"/> </ 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
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服务了:
另一种:
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 机制对这个服务进行变更,不会影响到项目的整体结构
版权声明:本文为qingtian878原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。