IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services  >

使用 MVC 样式的 Web 体系结构,第 1 部分:

同时静态和动态地访问服务

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

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
    可能有两个值 ― yesnoyes项表明 UDDI 中对相应服务执行动态查找,而 no则相反。
  • nextscreenid=必需的):
    这将根据服务类型识别要调用哪一屏。当 servlet 根据此参数动态调用相应的屏时,我们就需要这个属性了。

上面的“静态导航文件”中定义的变量 N 表明,可能有 N 个服务实现会提供相同的 Web 服务:该体系结构将为用户调用所有这些服务,并巩固所有这些服务的结果。





回页首


可视模型

本示例可以用可视的方式表示为 图 1中的模型。图形展示了本示例中涉及的各种组件之间的交互,并根据查找最便宜费用的过程中涉及的步骤编了号。 表 1按顺序对其中每一个步骤作出了解释。

表 1:“最便宜的飞机票价”(Cheapest Airfare)应用程序中的过程步骤

步骤 解释
1胖/瘦客户机选择所需的被查找的服务 ― 例如,英国和美国之间最便宜的飞机票价服务。
2servlet 创建 Service Manager 的一个新实例,并将请求和响应对象发送到 Service Manager。
3servlet 装入静态的 Service Mapping Navigator 文件,该文件包含服务类型的定义(例如最便宜的飞机票价服务)以及服务提供者和它的 urn。 这些服务是静态的,因为它们对系统来说都是已知的。
4Services Manager 通过实例化 formBean factory 调用相应的 formBean。formBean factory 从 http request 参数获取服务类型并调用相应的 formBean。例如,如果服务类型是 cheapestService,调用的 formBean 就是 cheapestServiceFormBean 。相应的 formBean 从 Httpservlet 请求参数检索用户输入的查询(例如美国和英国)并为自己植入数据。
5Services 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 美元等等。
7SOAP 客户机与 SOAP 服务器进行通信,并调用所需的服务。该逻辑是每个操作处理程序的一部分。
8、9、10如果导航文件中启用了动态 Web 服务查找,这就会起作用。UDDI 代理与 UDDI 注册中心进行通信,查找所有对应于服务名的商业实体,并查找它们的服务实现者的 URL、URN 和要调用的相应方法名 ― 然后调用服务实用程序类。
11服务实用程序为每个服务实现动态调用 SOAP 客户机,并传送服务实现 URL、URN 和方法名。服务实用程序负责动态创建 SOAP 请求,并从 SOAP 服务器取回响应。
12SOAP 客户机与 SOAP 服务器进行动态通信,并调用所需的服务。
13Service Manager 根据当前服务类型从 Static Services Mapping Navigator 文件获取下一屏的名称。
14ViewHandler factory 根据屏的名称调用相应的视图处理程序。
15ViewHandler 通过从通用数据对象检索数据为视图植入数据。
16如果在这些操作的过程期间出现了任何系统错误,控制权就会被转移到 Error Logger(错误日志程序),它会重定向至 Error Page(错误页)。
17最后,客户机将显示数据。


图 1:单个 Web 服务中的动态和静态接口
图 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与我联系。



参考资料



关于作者

Naveen Balani 目前在 Syntel India Ltd. 担任技术分析员。他目前的工作范围包括设计、开发和实现基于 J2EE 的产品。您可以通过 naveen_balani@syntelinc.com联系他。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款