下面开始介绍.NET2003下,如何利用反射、AOP、以及ASMX实现SOA:
一、实现机制
.NET2003的ASMX框架提供了Web Service 反射功能,如果按照平常的引用Web Service,IDE会自动生成一个Reference .cs文件的东西,里面实现了所引用的Web Service的反射调用类。其实我们可以自己来定义自己的反射类,来实现动态调用Web Service。比如直接建立一个类似Reference.cs文件的类,将里面的Web Service地址(服务地址)等当作参数传入(如虚线所示),实现动态绑定Web Service。然而有几个地方需要注意的:由于用了反射,每个Web Service方法的映射必须提供方法名、参数序列以及返回结果类型,特别是要考虑通用性时就得非常小心处理。但是,我们也应看到这个方案的优点,试想一下,各个系统提供的业务服务千差万别,服务地址、方法名、参数序列和返回结果都不可能相同,居于此方案,使该反射类能够尽可能的包含了各种业务方法的情况,从而实现反射的稳定性和适应性。
|
业务服务管理系统 WSMng |
|
业务系统A |
|
业务系统B |
|
业务系统XXX |
|
映射类 |
|
WS |
|
WS |
|
映射类 |
|
WS |
|
映射类 |
图2:
首先要考虑的是,必须为各个业务系统统一Web Service命名空间(该命名空间和类的命名空间含义是不一样的,可以参考MSDN帮助文档),在发布Web Service时,可以通过System.Web.Services.WebService属性来制定命名空间。对于一个开发商来说,当然能够自己定义自己的命名空间,这也不是什么难事,因此在规划面向服务结构体系时,应统一服务命名空间。对于不同的开发商来说,那就没办法了,只好为每个开发商来映射,当然这也是情理之中。
定义了服务的命名空间,则在反射类在反射Web Service方法的时候就方便多了。我们假设本公司统一定义面向服务体系结构的所有Web Service命名空间都为Company.WebService(此命名空间在映射类中需要用于描述http头,用于定位WebService,比如基于此命名空间,Company.WebService/GetServiceData则映射到方法GetServiceData,亦即SOAPAction定位),并开发和部署该Web Service服务。那么下面我们就应该考虑服务方法的实现了。
二、服务方法定义
接着考虑为服务方法定规范。由于服务方法必须要能够通用,因此方法名是必须预先定义的,而且只有一个;返回结果用最通用的DataSet返回,参数序列我们也用DataSet来传输。为什么要考虑定义一个唯一的服务方法,因为在反射机制之中,每一个方法的反射都必须提供方法名和参数列,如果业务方法很多而且不断的扩展,那么反射类也必须相应的修改,这使得通用性、维护性上变得非常差,不可能因为每上一个系统都要修改以前系统的代码来重新反射新的业务服务。为什么考虑DataSet,因为DataSet最容易封装和序列化,同时输出XML也非常容易,为了以后扩展成XML通讯十分便利。
假设当前Web Service服务xxx.asmx有如下的方法:DataSet CallServiceMethod (DataSet),用于获取当前服务业务的所有功能;DataSet GetServiceDetail()则是将当前有关业务方法(非Web Service方法,而是Web Service需要调用的其他业务方法)的信息按照规范提供出去,这些规范预先定义好的。因为Web Service的主要方法只有CallServiceMethod,所以只能通过参数的不同在后台用不同的业务方法处理,而参数和这些具体业务方法也必须能建立相应的对应关系。接下来的关键,就是要提供一个能够解释输出结果、参数序列的机制。注意,当其他业务系统需要用到当前业务系统的服务时,它必须利用已开发的反射类来调用,并且将当前服务的地址传入(动态调用,图2所示)。
|
业务系统A WS: xxx.asmx |
|
M1业务方法 |
|
M2业务方法 |
|
M3业务方法 |
|
CallServiceMethod方法 |
|
GetServiceDetail方法 |
图3
由于实际应用中,输出结果和参数的变化性比较大,可能是基本数据类型,也可能是数组、结构、对象等等,因此我们要对输出结果和参数要定一个说明书。说明书的DataSet(含结果DataTable和参数DataTable描述)将会解释了输出结果的所有信息结构,并能保障别的系统能拿到该说明书就知道用什么内容的参数调用CallServiceMethod,输出的结果是怎么样的,包含了什么,并能够转换成实际类型的结果。
为什么要用这种规范和对应关系,主要是为了考虑业务系统的后期绑定,开发时可以不用考虑最终的实现到底使用哪个Web Service,在实际应用的时候才配置绑定,虽然这个绑定需要手工配置(后面会详细解释),但对于业务的独立来说,仍然有价值。
三、说明文档获取方法定义
下面讨论这方法参数和返回结果两个规范的问题,也就是如何定义和解释这个说明书DataSet:含参数DataTable(可能多个)和结果DataTable。这些数据都通过GetServiceDetail()方法来获取。
首先,我们预先定义好规范。规范的说明文档记录方式:WSURL、明书DataSet。WSURL用于定位远程方法CallServiceMethod所在的WebService服务地址;参数DataTable用于记录该WS服务具体的业务方法,主要字段有MethodName、ParameterName、ParameterType、ParameterValue、Description;结果DataTable记录每个业务方法的返回结果的数据结构,主要字段有MethodName、ResultType、ResultValue、Description。在业务服务管理系统之中,相应的有一个存储规范的数据表,数据表保存URL和这个明书DataSet的XML描述,并且提供获取规范的方法(也是WS服务方法)。每个业务子系统(比如A)都需要在发布的时候调用该方法(GetServiceDetail方法实现) 获取规范信息,并用规范信息来解释业务子系统并形成说明文档,然后将说明文档发布到业务服务管理系统之中。或者通过业务服务管理系统自动询查注册在线的各个系统的说明文档。
|
业务服务管理系统WSMng |
|
业务系统A WS: xxx.asmx |
|
DataSet GetServiceDetail() |
|
说明文档管理 |
|
发布说明文档 PublishServiceDetail(WSMngURL) |
|
GetMethodDetail (当前服务类)) |
图4:
DataSet GetlMethodDetail(typeof(当前服务类))将获取当前服务所有业务方法的规范文档的整理,该方法利用自定义属性和映射的机制来收集当前服务的各个方法参数信息、结果信息,并封装成说明文档DataSet(包含含参数DataTable和结果DataTable)返回。
假设A系统有M1、M2、M3三个业务方法分别为:DataSet M1(DataSet Pa, int Pb)、bool M2(String,String)、void M3(),则说明文档DataSet应该如下:
方法的的参数表:MethodDetail
|
MethodName |
ParameterName |
ParameterType |
ParameterValue |
Description |
|
M1 |
Pa |
DataSet |
|
|
|
M1 |
Pb |
int |
|
|
|
M2 |
Pa |
String |
|
|
|
M2 |
Pb |
String |
|
|
|
M3 |
|
|
|
|
其中Mn表示实际的业务方法,如图3所示。
结果表:ResultDetSail
|
MethodName |
ResultType |
ResultValue |
ResultDescription |
|
M1 |
DataSet |
|
|
|
M2 |
bool |
|
|
|
M3 |
void |
|
|
说明书的传递方式,我们可以用一个固定的服务方法传递,比如DataSet GetServiceDetail(),该方法返回当前Web Service服务所有方法的说明(包括结果、参数)。
四、服务方法实现
好,接下来介绍如何通过WS服务方法调用业务方法。
业务子系统WS服务方法DataSet CallServiceMethod (DataSet)用于实现调用业务方法过程。要理解的是,WS只发布了WebMethod:CallServiceMethod,为什么没有发布其他业务方法,请参照第二节服务方法定义。服务方法基本上就是将参数DataSet按照自身的参数说明文档解释成实际的参数,再根据这些实际的参数进行相关的业务操作,然后将业务操作的结果按照结果说明封装成结果DataSet输出。
|
业务系统A WS: xxx.asmx |
|
参数PramDataSet |
|
DataSet CallServiceMethod (PramDataSet) |
|
M1 |
|
M2 |
|
M3 |
|
DataSet CallMethod(typeof(当前服务类), PramDataSet) |
图5:
其中DataSet CallMethod(typeof(当前服务类),DataSet)将实际的参数转换,并能根据参数DataSet调用真正的业务方法(M1~M3等),将结果按照规范封装输出。
其调用过程实际用了反射的原理,因为参数已经提供了方法名、方法的参数内容,所以利用反射可以调用相应的方法。
XXX.asmx实现代码片断:
……
[Nontriv.Attribute.WebService.ServiceMethod]
public System.Data.DataSet M1(
{
……
return ds;
}
[WebMethod]
public System.Data.DataSet CallServiceMethod(System.Data.DataSet parameter)
{
Nontriv.WebService.Reference.Criterion criterion=new Criterion();
return criterion.CallMethod(typeof(WebServiceTest),parameter);
}
……
其中WebserviceMethod为自定义属性,标记该方法为Webservice的业务方法。Criterion为规范文档类,用于收集当前WS类的有WebserviceMethod自定义属性的方法详细信息,并提供CallMethod方法调用业务方法。
五、服务管理系统
Now,我们已经实现了一个业务服务系统的基本功能,已经包含了如何制定服务,将服务发布到服务器,如何接收参数和返回结果。
下面讨论一下服务管理器如何实现管理这些服务,并能够很方便的给别的系统查询和配置。
和服务管理系统相关的最重要的就是说明文档管理系统了,其实这个说明文档管理也是一个WebService服务,不过由于这个服务比较特殊,没有涉及到客户的业务,因此这个服务是固定的,而且服务的方法也是固定的。
|
业务服务管理系统WSMng: xxx.asmx |
|
QueryWS(WSURL) |
|
AddOrUpdateWS(WSURL, DataSet) |
|
其他管理 |
|
业务系统A WS:xxx.asmx |
|
GetServiceDetail |
|
PublishServiceDetail |
说明文档包含了各个注册业务服务的规范说明,包括WS地址、包含方法参数和结果的说明文档DataSet。其主要功能是提供查询,便于配置本地方法和WS业务服务方法对应,同时还为以后执行一些任务式的调用作准备。比如可以让业务服务管理系统定时调用某个业务服务的某个方法。
发布到服务管理器有两条途径,一是通过在服务管理系统中输入业务服务WS的地址,系统将自动询查业务服务系统的所有业务方法及输出结果的说明;另外一种就是业务系统输入服务器地址,业务系统将自动将自己的说明注册到服务器。这辆条路径都需要人工操作,目前还无法实现自动询查和注册,主要还要考虑路由等,比较难实现。
六、调用服务和接受服务结果
最后考虑如何接收服务以及处理服务返回的结果。
调用方和服务方类似,也就是将参数通过将调用参数说明构造成DataSet,然后调用DataSet CallServiceMethod (DataSet),再利用结果说明将结果解释出来。注意的是,这里的CallServiceMethod是本地对远程WS的反射类,记得前面我们介绍过,WS的反射必须方法名字、参数序列等必须要一致。
在些调用WebService业务方法时,必须要准备这两项重要工作:获取到映射类,用于将WebService类映射到本地类,并通过动态配置WSURL来实现动态调用业务服务;AOP面向方面框架,用于捕获本地方法调用参数,并将这些参数提取出来,并用于映射类调用远程服务方法时所需的参数拟定;和服务方一样,也需要一个能够获取到本地方法的说明文档,这些说明文档表示了哪些方法需要调用远程方法,以便后期可以根据这些信息来绑定具体的WS方法。
参数对应配置,由本地配置并保存在本地XML中。保存字段主要有:WSURL、WebService业务方法及参数序列、本地方法及参数序列。这个配置过程,是由手工配置的。可以通过在业务管理系统中查出WebService以及其说明文档,用户再根据本地开发时的调用方法的参数文档进行一一配对。
举个开发例子。
比如在开发某业务系统时,我们需要用到权限验证,在权限验证的方法中,我们实现如下:
[Nontriv.WebService.AOP.WSAOP]
public class ClientOfWS:Nontriv.WebService.AOP.WSCountexBoundObject
{
[Nontriv.Attribute.WebService.ClientMethod]
public bool HaveQX(string Name,string qx)
{
return (bool)reflect.GetData(this.GetParamDS());
}
}
和WS服务实现有点类似,其中Nontriv.WebService.AOP.WSAOP表示该类自定义了属性,也表示了该类实现了AOP框架(继承了Nontriv.WebService.AOP.WSCountexBoundObject)。
由于使用了AOP框架,方法在调用时捕捉到实际的调用参数,并根据说明文档格式将参数转换成DataSet(包含了含参数DataTable和结果DataTable),如果参数是DataSet,则将该DataSet的数据表一一拆离,并复制到说明文档的DataSet之中,并标记它是属于哪一个参数的。这些参数信息,我们用this.GetParamDS()来获取,有点像获取当前上下文对象的信息,不过这里面的实现是结合了AOP框架,使用了Remoting的上下文消息监听机制。
HaveQX方法的实现,我们只用了WS映射类的一个固定方法GetData,此方法封装了之前我们讨论的CallServiceMethod方法(asxm的反射方法,用于反射远程Webservice的CallServiceMethod方法),由于CallServiceMethod返回的是一个封装了结果的DataSet,因此GetData方法还需对结果进行转换。另外,此方法还根据配置情况对实际参数进行转换,后面会介绍怎么转换。
this.GetParamDS()得到的数据大概如下(DataSet数据结构参见规范及说明描述):
MethoDetail表:
|
MethodName |
ParameterName |
ParameterType |
ParameterValue |
Description |
|
HaveQX |
Name |
string |
实际的值 |
|
|
HaveQX |
qx |
string |
实际的值 |
|
ResultDetail表:
|
MethodName |
ResultType |
ResultValue |
Description |
|
HaveQX |
bool |
空 |
|
假设我们需要将本地方法HaveQX和前面所定义的远程方法M2绑定,配置文件如下:
<PrmDataSet>
<PrmDataTable>
<LocalMethodName>HaveQX</LocalMethodName>
<LocalMethodPrm>Name,qx</LocalMethodPrm>
<WSURL>http://……/XXX.asmx</WSURL>
<WSMethodName>M2</WSMethodName>
<WSMethodPrm>Pa,Pb</WSMethodPrm>
</PrmDataTable>
</PrmDataSet>
这个配置文件表示本地方法HaveQX调用了某个WebService业务方法M2,在调用过程中,将HaveQX的参数Name传给M2的Pa,将HaveQX的参数qx传给M2的Pb。配置过程的大概是通过配置页面进行配置的,具体做法大概是用户得到了业务服务管理系统的注册服务信息,将它们列出来,用户经过选择并配置到本地的业务系统方法之中。
经过上面配置后,则反射类的GetData方法会跟据这个说明文档将参数转换成如下参数数据:
MethoDetail表:
|
MethodName |
ParameterName |
ParameterType |
ParameterValue |
Description |
|
M2 |
Pa |
string |
原Name实际的值 |
|
|
M2 |
Pb |
string |
原qx实际的值 |
|
ResultDetail表:
|
MethodName |
ResultType |
ResultValue |
Description |
|
HaveQX |
bool |
空 |
|
本地调用方法的参数传送到远程WS的CallServiceMethod方法之后,CallServiceMethod调用了 CallMethod,该方法根据参数表调用具体的业务方法,并将结果填充到ResultDetail表。如果返回结果是DataSet,类似的也会将DataSet里面的表拆离并复制到CallMethod返回结果的DataSet之中。
之后的事情就是将这个结果返回给客户端,再由客户端根据结果类型来转换远程所返回的DataSet里面的内容。
综上所述,本文已经从各个方面描述了WebService实现SOA平台的基本功能,但也有一些缺陷在里面:规范定义和说明文档并不能描述得了所有参数类型和返回结果,特别是DataSet的情况。在实际的处理DataSet,基本上都是需要知道列的信息,而本文并没有好办法通过规范和说明并无法描述列的信息(包括列信息的映射和数据的映射)。此外对于非序列化的数据也不能很好的支持,比如参数或者结果如果是ArrayList、HashTable的情况下,则无法传送。
此外本平台的实现肯定还有很多劣势,其中关于AOP的实现就不是很理想,需要利用到.NET的Remoting消息监听机制。另外对于传大数据来说,本平台还是显得不适合。
但其优势我们还是可以一一道出,对于简单业务方法,我们这方面的封装还是有优势,特别是解决了系统与系统、组件与组件的依赖降低问题,系统的松耦合使得业务系统的扩展灵活和互影响降到最低,另外如果在服务管理系统植入了流程管理系统,则企业级的业务流整合平台还是能够在一定程度上实现。这个话题,以后会慢慢探讨并付诸实现。
浙公网安备 33010602011771号