使用MVC样式的Web体系结构:第1部分
--同时静态和动态地访问服务
Naveen Balani (
naveen_balani@syntelinc.com)
技术分析员,Syntel
India Ltd.
2002 年 2 月
模型-视图-控制器(Model-View-Controller,MVC)模式在面向对象应用程序的软件工程中相当有用。本文将讨论如何将其应用到静态或动态调用的
Web 服务中去。
Web 服务可以使用 WSDL 服务接口和服务实现文档来静态调用,也可以通过 UDDI
检索服务类型定义和服务实现来动态调用。但您以前一直不能同时实现这二者。现在,您可以使用模型-视图-控制器模式(或者说
MVC)来实现这一点;这种体系结构同时支持动态和静态 Web 服务。本文主要是为了让您练习设计,它假定您对设计模式和 MVC
系统有所了解。请参阅参考资料以了解更多关于 MVC 的信息。
MVC 范例是一种拆分方法,它将应用程序(甚至只是应用程序的一个接口)拆分成三个部分:模型、视图和控制器。
模型表示企业数据和管理对该数据的访问和更新的业务规则。通常,模型充当现实世界中的过程的软件模拟,这样,在定义模型的时候就可以应用真实世界的建模技术了。
视图处理模型的内容。它通过模型访问企业数据,并指定应该如何表示该数据。
在模型发生改变时,视图将负责在它的表示中保持一致性。这可以通过使用推(push)模型(视图向该模型注册,以获取它的改变通知)来实现,也可以用拉(pull)模型(此时视图负责在需要检索最新数据时调用模型)来实现。
控制器将和视图之间的交互转换为由模型执行的操作。在独立的 GUI 客户机中,用户交互可能是按钮单击或菜单选择,然而在 Web
应用程序中,它们则可能是 GET 和 POST HTTP
请求。由模型执行的操作包括激活业务流程或改变模型状态。控制器根据用户交互和模型操作的结果选择合适的视图,从而作出响应。
MVC 体系结构有下面的好处:
多个视图使用同一个模型。模型和视图的分开使多个视图可以使用相同的企业模型。因此,企业应用程序的模型组件就更容易实现、测试和维护了,因为所有对模型的访问都要经过这些组件。
对客户机的新类型更容易支持。要支持客户机的新类型,您只需为其编写一个视图和控制器,然后在已有的企业模型中将它们连起来就可以了。
请注意,本文假定您已经熟悉了 Web 服务、SOAP、XML 和 UDDI —
如果您并不熟悉它们,请参阅本文的参考资料部分,那里有关于这里所有内容的在线信息的链接。
Web 服务中 MVC
的一个示例
请考虑这样一个示例,一个托管站点可以根据用户的查询找到两地间最便宜的飞机票价。托管站点查找已知的 Web
服务,这些服务被注册在“静态导航服务”(Static Navigation Service)文件中。这是一个 Web 服务的静态查找,因为 Web
服务接口和实现对系统来说是已知的。
“静态导航服务”文件是一个 XML 文件,它含有类似于下面所示的条目:
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 与我联系。
参考资料
请单击文章顶部或底部的讨论参加关于本文的讨论论坛。
要了解关于 Web 服务的更多信息,请参阅Web Services architectural overview。
developerWorks 的系列文章 Web services architect 将建立 Web 服务技术的坚实基础。
要了解更多关于 SOAP 的信息,请参阅 SOAP 规范。
您可能还会对 developerWorks 上的这篇文章感兴趣 — Myths and misunderstandings surrounding SOAP。
uddi.org 的技术白皮书也有很多关于 UDDI 的信息(PDF 格式)。
The role of private UDDI nodes in Web services 以
developerWorks 的一系列文章来描述;这里是该系列的第一部分。
Uniform
Resource Names (urn) 宪章。
MVC
模式:用于将逻辑与接口分开,并将二者与方法分开。它最初用在 Smalltalk 中,但在其它很多地方也很有用,正如 Steve Burbeck 在本文中所解释的情况。
关于 MVC还有更多信息。
如果您已经熟悉 Java Swing,您就已经熟悉 MVC 了。
MVC 中 Observer
连接的常见问题导致了另一种模式的开发 — 模型-视图-表示器,或者说 MVP。
MVP 是连接
IBM 的主要面向对象语言环境的线程。
包括 WebSphere Application Server,它也要利用 MVC 模式。
Patterns
Home Page 上有关于软件模式和模式语言所有方面的信息。
Wiki 也有很多关于模式的信息。
Christopher Alexander
等人所著的A
Pattern Language: Towns, Buildings, Construction是关于模式的经典作品,该书由牛津大学出版社于 1977 年出版。
关于作者
Naveen Balani 目前在 Syntel India Ltd.
担任技术分析员。他目前的工作范围包括设计、开发和实现基于 J2EE 的产品。您可以通过 naveen_balani@syntelinc.com 联系他。
浏览: 使用MVC样式的Web体系结构:第2部分
如果您希望与本文章的作者或其所在机构,进一步交流,请联系:畅享网 姜小姐
jill.jiang@amteam.org | 021-51096826-112 |
在线联系