级别: 初级 Naveen Balani (naveen_balani@syntelinc.com), 技术分析员
2002 年 2 月 01 日 模型-视图-控制器(Model-View-Controller,MVC)模式在面向对象应用程序的软件工程中相当有用。本文将讨论如何将其应用到静态或动态调用的 Web 服务中去。
Web 服务可以使用 WSDL 服务接口和服务实现文档来静态调用,也可以通过 UDDI 检索服务类型定义和服务实现来动态调用。但您以前一直不能同时实现这二者。现在,您可以使用模型-视图-控制器模式(或者说 MVC)来实现这一点;这种体系结构同时支持动态和静态 Web 服务。本文主要是为了让您练习设计,它假定您对设计模式和 MVC 系统有所了解。请参阅参考资料以了解更多关于 MVC 的信息。
MVC 范例是一种拆分方法,它将应用程序(甚至只是应用程序的一个接口)拆分成三个部分:模型、视图和控制器。
模型表示企业数据和管理对该数据的访问和更新的业务规则。通常,模型充当现实世界中的过程的软件模拟,这样,在定义模型的时候就可以应用真实世界的建模技术了。
视图处理模型的内容。它通过模型访问企业数据,并指定应该如何表示该数据。
 |
Web 服务和 Web 服务技术
-
Web 服务:
自我包含、自我描述的模块化应用程序,可以借助下面的开放标准技术在 Web 上发布、定位和调用
-
SOAP:
简单对象访问协议(Simple Object Access Protocol),也被称为面向服务的体系结构协议;基于 XML 的 RPC 和消息传递协议
-
WSDL:
Web 服务描述语言(Web Service Description Language),它是用于描述 Web 服务的描述性接口和协议绑定语言
-
UDDI:
通用描述、发现和集成(Universal Description, Discovery, and Integration),它是可以用于查找 Web 服务描述的一种注册中心机制
|
|
在模型发生改变时,视图将负责在它的表示中保持一致性。这可以通过使用推(push)模型(视图向该模型注册,以获取它的改变通知)来实现,也可以用拉(pull)模型(此时视图负责在需要检索最新数据时调用模型)来实现。
控制器将和视图之间的交互转换为由模型执行的操作。在独立的 GUI 客户机中,用户交互可能是按钮单击或菜单选择,然而在 Web 应用程序中,它们则可能是 GET 和 POST HTTP 请求。由模型执行的操作包括激活业务流程或改变模型状态。控制器根据用户交互和模型操作的结果选择合适的视图,从而作出响应。
MVC 体系结构有下面的好处:
- 多个视图使用同一个模型。模型和视图的分开使多个视图可以使用相同的企业模型。因此,企业应用程序的模型组件就更容易实现、测试和维护了,因为所有对模型的访问都要经过这些组件。
- 对客户机的新类型更容易支持。要支持客户机的新类型,您只需为其编写一个视图和控制器,然后在已有的企业模型中将它们连起来就可以了。
- 请注意,本文假定您已经熟悉了 Web 服务、SOAP、XML 和 UDDI ― 如果您并不熟悉它们,请参阅本文的
参考资料部分,那里有关于这里所有内容的在线信息的链接。
Web 服务中 MVC 的一个示例
请考虑这样一个示例,一个托管站点可以根据用户的查询找到两地间最便宜的飞机票价。托管站点查找已知的 Web 服务,这些服务被注册在“静态导航服务”(Static Navigation Service)文件中。这是一个 Web 服务的静态查找,因为 Web 服务接口和实现对系统来说是已知的。
“静态导航服务”文件是一个 XML 文件,它含有类似于下面所示的条目:
<service type="CheapestAirFare" serviceimplemenation1="http://airservice.com/rrpcrouter"
serviceimplemenation1urn1="urn:fare" methodName1 ="getRate" ..
serviceimplemenationN= " " . serviceimplemenation1urnN=" " methodNameN="getCheapestRate"
dynamicLookup="Yes"
nextscreenid= " CheapestAirFare" >
|
下面给出了对每个条目的解释:
-
type (
必需的):
当用户选择服务时,它的类型在一个隐藏的参数字段中被设置,这个参数字段将被传送到控制器 servlet。被设置的类型对应于该文件中被映射的条目。这个属性是必需的,因为 servlet 根据该参数动态地创建并调用 Web 服务。
-
serviceimplemenation1 :
第一个静态服务目标 URL,第一个 Web 服务就位于此 URL 中。
-
serviceimplemenation1urn1 :
第一个静态服务目标 URN(统一资源名,Uniform Resource Name)。只有 URN 为客户机唯一的标识服务。
-
methodName1 :
调用
serviceimplemenation1urn1 的相应 URN 的方法名。
-
dynamicLookup :
可能有两个值 ―
yes或
no。
yes项表明 UDDI 中对相应服务执行动态查找,而
no则相反。
-
nextscreenid= (
必需的):
这将根据服务类型识别要调用哪一屏。当 servlet 根据此参数动态调用相应的屏时,我们就需要这个属性了。
上面的“静态导航文件”中定义的变量 N 表明,可能有 N 个服务实现会提供相同的 Web 服务:该体系结构将为用户调用所有这些服务,并巩固所有这些服务的结果。
可视模型
本示例可以用可视的方式表示为
图 1中的模型。图形展示了本示例中涉及的各种组件之间的交互,并根据查找最便宜费用的过程中涉及的步骤编了号。
表 1按顺序对其中每一个步骤作出了解释。
表 1:“最便宜的飞机票价”(Cheapest Airfare)应用程序中的过程步骤
|
步骤
|
解释
| | 1 | 胖/瘦客户机选择所需的被查找的服务 ― 例如,英国和美国之间最便宜的飞机票价服务。 | | 2 | servlet 创建 Service Manager 的一个新实例,并将请求和响应对象发送到 Service Manager。 | | 3 | servlet 装入静态的 Service Mapping Navigator 文件,该文件包含服务类型的定义(例如最便宜的飞机票价服务)以及服务提供者和它的 urn。 这些服务是静态的,因为它们对系统来说都是已知的。 | | 4 | Services Manager 通过实例化 formBean factory 调用相应的 formBean。formBean factory 从
http request 参数获取服务类型并调用相应的 formBean。例如,如果服务类型是 cheapestService,调用的 formBean 就是
cheapestServiceFormBean 。相应的 formBean 从
Httpservlet 请求参数检索用户输入的查询(例如美国和英国)并为自己植入数据。
| | 5 | Services Manager 使用 ActionHandler Factory 根据服务类型和方法名(N)实例化对应的 ActionHandler。例如,如果服务类型是 cheapestAirFare 而方法名是
getRate ,那么 ActionHandler factory 就会实例化
cheapestAirFaregetRateActionHandler 。对于相应的服务类型来说,可能有很多服务实现 ― 因此应该有相同数量的操作处理程序(action handler),因为每个操作程序都可以执行不同的逻辑。至于返回类型,对某些服务来说它可能是一个简单的字符串 ― 对其它服务来说它则可能是一个 XML 文档。所以,相应的操作处理程序必须处理这些类型,并最终返回一个公共类型 ― 例如一个字符串。在本这里,这个字符串可能表示一个价格,如 3000 美元。
| | 6 | 现在 Services Manager 调用 ActionHandler 的一个方法,该方法将执行各种不同操作,如调用 SOAP Client 和传送所需的服务实现者 URL、URN 和方法名。Services Manager 还负责将其从 SOAP 客户机收到的结果放到通用数据中 ― 其中包含来自所有操作处理程序的结果。例如,对于
serviceimplemenation1urn1 最便宜的飞机票价可能是 3000 美元;对于其它的就可能是 3200 美元等等。
| | 7 | SOAP 客户机与 SOAP 服务器进行通信,并调用所需的服务。该逻辑是每个操作处理程序的一部分。 | | 8、9、10 | 如果导航文件中启用了动态 Web 服务查找,这就会起作用。UDDI 代理与 UDDI 注册中心进行通信,查找所有对应于服务名的商业实体,并查找它们的服务实现者的 URL、URN 和要调用的相应方法名 ― 然后调用服务实用程序类。 | | 11 | 服务实用程序为每个服务实现动态调用 SOAP 客户机,并传送服务实现 URL、URN 和方法名。服务实用程序负责动态创建 SOAP 请求,并从 SOAP 服务器取回响应。 | | 12 | SOAP 客户机与 SOAP 服务器进行动态通信,并调用所需的服务。 | | 13 | Service Manager 根据当前服务类型从 Static Services Mapping Navigator 文件获取下一屏的名称。 | | 14 | ViewHandler factory 根据屏的名称调用相应的视图处理程序。 | | 15 | ViewHandler 通过从通用数据对象检索数据为视图植入数据。 | | 16 | 如果在这些操作的过程期间出现了任何系统错误,控制权就会被转移到 Error Logger(错误日志程序),它会重定向至 Error Page(错误页)。 | | 17 | 最后,客户机将显示数据。 |
图 1:单个 Web 服务中的动态和静态接口
这种系统的主要优势在于,对于基于组件的开发或整个体系结构保持一致的服务来说,这种设计非常好,而且组件可以轻易地被插入。
清单 1 是一些示例代码,它将展示 ActionHandler factory 看起来可能是什么样的。在本示例中,
IActionHandler 是一个被所有操作处理程序扩展的接口。
清单 1:示例 ActionHandler Factory
public class ActionHandlerFactory {
public IActionHandler getActionHandler(String serviceType ,
String methodName)
{
IActionHandler iaction = null;
try
{
String className = serviceType + methodName + "ActionHandler";
iaction = (IActionHandler) Class.forName(className).newInstance();
}
catch (Exception e) {
e.printStackTrace();
}
return iaction;
}
}
|
清单 2是一个代码片段,它展示了操作处理程序看起来可能是什么样的。
清单 2:示例操作处理程序代码
public class cheapestAirFaregetRateActionHandler implements IActionHandler {
public void performProcess(Formbean formbean, String serviceUrl, String serviceUrn,
String methodName, CommonData commonData)
{
//Type cast FormBean to current formbean i.e cheapestServiceFormBean
cheapestServiceFormBean chServiceformBean = (cheapestServiceFormBean) FormBean
//Get the destinations name from chServiceformBean
String country1 = chServiceformBean.getCountry1();
String country2 = chServiceformBean.getCountry2();
// Create and initialize the org.apache.soap.rpc.Call object.
Call call =new Call();
//Set the target URI .Method Name of service implementation1
// serviceUrn = urn:fare
call.setTargetObjectURI (serviceUrn );
// methodName=getRate
call.setMethodName (methodName );
Vector params =new Vector();
//Populate the parameter to be passed to soap server
Parameter country1Param =new Parameter(
"country1",String.class,country1, ",Constants.NS_URI_SOAP_ENC);
params.addElement(country1Param);
Parameter country1Param =new Parameter(
"country2",String.class,country2,Constants.NS_URI_SOAP_ENC);
params.addElement(country2Param);
call.setParams(params);
Response resp =null;
// Set the url of service implemenation1
// serviceUrl = http://airservice.com/rrpcrouter
URL url =new URL (serviceUrl);
// Invoke the corresponding service
Resp =call.invoke (serviceUrl, serviceUrn);
Parameter result=resp.getReturnValue();
//Get the result object from soap server
Object o = result.getValue();
// Do processing on result to get back the result in string , and put the data in
// commonData
}
}
|
到这里,体系结构就完成了。这样,您成功地使用 MVC 体系结构并应用它调用了静态和动态 Web 服务。如果您就本文的内容给我写信,我将非常感兴趣。请随时通过
naveen_balani@syntelinc.com与我联系。
参考资料
关于作者
对本文的评价
|