Web 服务 1/66 高级软件工程 第 五 讲 Web 服务
Web 服务 2/66 高级软件工程 内 容 一、 Web 服务简介 二、 SOAP 三、 WSDL 四、支持 Web 服务的 J2EE 应用
Web 服务 3/66 高级软件工程 一、 Web 服务简介 2000 年 Microsoft 等提出 “Web Services” Web 服务( Web Services ) 是基于 XML 和 HTTP 的一种服务 服务访问协议: SOAP 服务的描述: WSDL 服务查找与发现: UDDI
Web 服务 4/66 高级软件工程 SOAP (Simple Object Access Protocol) – XML-based (text) 协议 – 支持远程通信 – 平台中立 WSDL (Web Services Definition Language) – 接口描述 UDDI (Universal Description, Discovery, and Integration) – 用于 Web Services 注册 – 用于发现 Web Services
Web 服务 5/66 高级软件工程 客户 / 服务请求者 服务提供者 注册 请求 / 应答 查找 基本结构: 服务信息注册库
Web 服务 6/66 高级软件工程 提供了软件模块之间一种松耦合的交互方式 根据需求通过网络对松散耦合的粗粒度应用组件 进行分布式部署、组合和使用 服务层是 SOA 的基础,可以直接被应用调用,从 而 有效控制系统中与软件代理的人为依赖性 SOA 的几个关键特性:一种粗粒度、松耦合服务 架构,服务之间通过简单、精确定义适配器进行 通讯,不涉及 底层编程适配器和通讯模型
Web 服务 7/66 高级软件工程 RPC vs. Document RPC – 耦合密切些 相对脆弱 – 仅仅是调用 Document – 耦合松散些 对应用修改适应性好 没有序列化 / 反序列化问题 – 需要额外的设计工作 需要解释客户消息内容,然后进行相应操作
Web 服务 8/66 高级软件工程 考虑 Web Services 的几个理由 业务上 – 需要与外部客户通信 技术上 – 应用需要与 其它语言编写的 客户程序通信 – 客户在防火墙之外 管理上 – 管理托管 web service 应用
Web 服务 9/66 高级软件工程 什么时候不要使用 Web Services 客户程序与应用使用相同语言编写 通信开销大 – 序列化或者远程访问开销大 – Web Services/XML 处理开销大 – “Don’t Use XML to Communicate Unless You Really, Really Have To” – Floyd Marinescu, The Middleware Company 永远记住: Web Services/XML 是用于集成的
Web 服务 10/66 高级软件工程 Apache Axis (Apache eXtensible Interaction System) 是 Apache WebService 项目中的子项目 最初起源于 IBM 的 "SOAP4J“ 最早的一批用于构造基于 SOAP 应用的 Framework JBoss, WAS 等重要的应用服务器都集成它 典型的 Web 服务支持平台
Web 服务 11/66 高级软件工程 服务器端 AXIS 句柄链处理架构 用户配置一系列的 handler ,构成 handler chain AXIS 依次调用 handler 处理消息(含 context ) 类似于 interceptor (截取器)
Web 服务 12/66 高级软件工程 客户端 AXIS 句柄链处理架构
Web 服务 13/66 高级软件工程 AXIS 的子系统
Web 服务 14/66 高级软件工程 二、 SOAP 1 、 SOAP 概述 2 、数据表示 3 、消息格式 4 、协议映射
Web 服务 15/66 高级软件工程 1 、 SOAP 概述 许多程序通过使用远程过程调用( RPC ) 在诸如 DCOM 与 CORBA 等对象之间进行通信 但 RPC 会产生兼容性以及安全问题; 防火墙和代理服务器通常会阻止此类流量 通过 HTTP 在应用程序间通信是更好的方法 因为 HTTP 得到了所有的因特网浏览器及服务器的支持 SOAP 就是被创造出来完成这个任务的
Web 服务 16/66 高级软件工程 2000 年 5 月, UserLand 、 Ariba 、 Commerce One 、 Compaq 、 Developmentor 、 HP 、 IBM 、 IONA 、 Lotus 、 Microsoft 以及 SAP 向 W3C 提交了 SOAP 因特网协议 期望此协议能够通过使用因特网标准( HTTP 以及 XML )把图形用户界面桌面应用程序连接到因特网 服务器 首个关于 SOAP 的公共工作草案由 W3C 在 2001 年 12 月发布 SOAP 1.2 于 2003 年 6 月 被发布为 W3C 推荐标准
Web 服务 17/66 高级软件工程 简单对象访问协议( SOAP ) 是网络环境中交换信息的简单协议 为网络环境下软件之间结构化、类型化信息的交换 提供了一种基于 XML 的机制 它可以广泛地用于基于消息的系统和基于 RPC 的系统 SOAP 被设计为可以与各种其它协议结合使用 但目前 SOAP 主要和 HTTP 及 HTTP 扩展框架相结合
Web 服务 18/66 高级软件工程 简单地讲, SOAP= HTTP+RPC+ XML 即 : SOAP 以 HTTP 作为底层通讯协议 以 RPC 作为一致性的调用途径 以 XML 作为数据传送的格式 SOAP 的设计原则是 : 简单、易于扩展 SOAP 的设计忽略了如下几方面的功能: ( 1 )分布式垃圾回收 ( 2 )消息的批处理 ( 3 )对象引用 ( 4 )对象激活
Web 服务 19/66 高级软件工程 SOAP 的数据表示完全不同于以往互操作协议的表示方法 以往的互操作协议都将调用语句编排为 二进制的字节流的形式 SOAP 采用 XML 作为自己的数据表示方法 XML 是与 HTML 类似的基于文本的标记语言 SOAP 将调用语句编排为文本式的字符流的形式 2 、数据表示
Web 服务 20/66 高级软件工程 SOAP 消息是一个 XML 文档 包括: 一个必需的 SOAP 封装 一个可选的 SOAP 头 一个必需的 SOAP 体 3 、消息格式
Web 服务 21/66 高级软件工程 SOAP 封装定义了描述信息和如何处理信息的框架 用于指定用 XMLSchema 来描述 XML 数据的编码规则 封装可以包含名域声明和附加属性 如果包含附加属性,这些属性必须限定名字域 类似的, “Envelope” 可以包含附加子元素 这些也必须限定名字域且跟在 SOAP 体元素之后 ( 1 ) SOAP 封装
Web 服务 22/66 高级软件工程 SOAP 消息头是 SOAP 消息的可选部分 用来扩展其它诸如安全、事务等服务的重要机制 如果出现的话,必须是 SOAP 封装元素的第一个直接子元素 SOAP 头可以包含多个 SOAP 块 每个都是 SOAP 头元素的直接子元素 所有 SOAP 头的直接子元素都必须限定名字域 ( 2 ) SOAP 消息头
Web 服务 23/66 高级软件工程 SOAP 体在 SOAP 消息中必须出现 且必须是 SOAP 封装元素的直接子元素 SOAP 体可以包括多个条目 每个条目必须是 SOAP 体元素的直接子元素 SOAP 体元素的直接子元素可以限定名字域 ( 3 ) SOAP 体
Web 服务 24/66 高级软件工程 SOAP 自然地遵循 HTTP 的请求 / 应答消息模型 使得 SOAP 的请求和应答参数可以包含在 HTTP 请求和应答中 SOAP HTTP 遵循 HTTP 中 表示通信状态信息的 HTTP 状态码的语义 例如, 2xx 状态码表示 这个包含了 SOAP 组件的客户请求 已经被成功的收到、理解和接受 下页的代码是一个使用 POST 的 SOAP HTTP 例子 该消息通过 HTTP 发出一条请求 “ 获取股票代码为 ABC 的最新交易价格 ”
Web 服务 25/66 高级软件工程 POST /StockQuote HTTP/1.1 Host: Content-Type: text/xml; charset="utf-8" Content-Length: nnnn SOAPAction: "Some-URI" <SOAP-ENV:Envelope xmlns:SOAP-ENV=" SOAP-ENV:encodingStyle=" 5 ABC
Web 服务 26/66 高级软件工程 1 、互操作开销不同 IIOP 、 JRMP 使用二进制的字节流形式编排消息 ( CDR 、 XDR ) SOAP 采用字符型的 XML 编排消息 SOAP 消息要比 IIOP 、 JRMP 消息长得多 SOAP 的编排开销大、占用内存空间大 2 、表达能力不同 SOAP 易于学习、易于开发、易于调试 SOAP 不支持消息的批处理、对象引用、对象激活等特性 降低了 SOAP 的表达能力 并使得基于 SOAP 的交互受到限制 而 IIOP 、 JRMP 等则不存在这种限制 与其它协议的比较
Web 服务 27/66 高级软件工程 3 、适应能力不同 IIOP 、 JRMP 严重制约于防火墙 防火墙一般由两个路由器与一个应用程序网关构成 路由器负责 IP 层的分组信息 列出可接受、禁止的源端和目标端等信息 应用程序网关在应用程序级进行控制 根据头信息字段、消息长度、消息内容等 决定传送还是丢弃消息 这是 IIOP 、 JRMP 的应用受到限制的核心因素之一 SOAP 则基本不受其限制 4 、适用环境不同 JRMP 适用于使用 JAVA 的应用系统 IIOP 、 SOAP 支持各种语言,因此适用面更广 IIOP 适合于同一个防火墙内部之间的交互 而 SOAP 则适合于跨越防火墙的交互
Web 服务 28/66 高级软件工程 三、 WSDL 1 、概述 2 、文档结构 3 、例子 4 、向 SOAP 的映射
Web 服务 29/66 高级软件工程 1 、概述 WSDL ( Web Services Description Language )是一个建 议性标准 在 Microsoft 的 SDL ( Service Description Language 和 SCL ( SOAP Contract Language )和 IBM 的 NASSL ( Network Accessible Service Specification Language ) 这两项技术的结合,形成了 WSDL 的基础 2000 年 9 月 25 日 IBM 、 Microsoft 和 Ariba 提出 WSDL 年 3 月 15 日,他们提交的 WSDL1.1 成为 W3C 的 Note 2002 年 7 月 9 日提出 WSDL 年 11 月 10 日提出 WSDL 2.0
Web 服务 30/66 高级软件工程 类型 消息 端口类型 绑定 服务 操作 端口 抽 象 定 义 具 体 说 明 WSDL 文档 代表依赖关系 代表包含关系 2 、文档结构
Web 服务 31/66 高级软件工程 类型:独立于计算机和语言的类型定义 使用某一类型系统(例如 XSD )进行数据类型定义的容器 用于描述被交换的消息 消息:对通信数据的一个抽象、类型化定义 一个消息包含多个逻辑部分 每一个都与某一个类型系统中的定义相关联 包含函数参数(输入与输出分开)或文档说明 端口类型:由一个或多个端点支持操作的抽象集合 每个操作对应于一个输入消息与一个输出消息 它引用消息节中的消息定义来说明函数基调 操作名称、输入参数和输出参数 等 ( 1 )抽象定义
Web 服务 32/66 高级软件工程 绑定:为一个由特定端口类型定义的操作与消息 指定具体的协议及数据格式规范 服务:指定每个绑定的端口地址 ( 2 )具体说明
Web 服务 33/66 高级软件工程 <definitions name="FooSample" <schema targetNamespace=" xmlns=" xmlns:SOAP-ENC=" xmlns:wsdl=" elementFormDefault="qualified" > 3 、例子
Web 服务 34/66 高级软件工程 <soap:body use="encoded" namespace=" encodingStyle=" /> <soap:body use="encoded" namespace=" encodingStyle=" />
Web 服务 35/66 高级软件工程 interface FooSample { long foo(long arg); } 用 OMG-IDL 表示为:
Web 服务 36/66 高级软件工程 对应的 SOAP 请求消息为: <SOAP-ENV:Envelope SOAP-ENV:encodingStyle=" xmlns:SOAP-ENV=" 、向 SOAP 的映射
Web 服务 37/66 高级软件工程 <SOAP-ENV:Envelope SOAP-ENV:encodingStyle=" xmlns:SOAP-ENV="
Web 服务 38/66 高级软件工程 与其它描述方法的比较 有的以描述结构化程序的功能为主 例如 RPC-IDL 微软的 -IDL 有的以描述对象的功能为主 例如 CORBA 的 IDL 有的以描述服务为主 例如 web service 的 WSDL 1 、描述对象不同
Web 服务 39/66 高级软件工程 有的以具体计算机语言的方式表达 例如 SUN 的 Java Interface 有的以独立于具体的计算机语言 但十分类似于计算机语言的方式表达 例如: RPC-IDL 微软的 -IDL CORBA 的 IDL 有的以 XML 为方式表达 例如: web service 的 WSDL 2 、描述方式不同
Web 服务 40/66 高级软件工程 有的仅描述接口的语法信息 例如: RPC-IDL 微软的 -IDL CORBA 的 IDL 有的还包括与底层协议的绑定信息 例如: WSDL 等 在 CORBA 中这部分信息包含在 IOR 中 3 、描述内容不同
Web 服务 41/66 高级软件工程 如何为 web 系统增加 web services 接口 下面模块可以暴露为 Web Services: – EJB – POJO 后面的例子针对 EJB 四、支持 Web 服务的 J2EE 应用
Web 服务 42/66 高级软件工程 JAX-RPC: Java API for XML-based RPC Java 世界的 web service 编程模型规范 如何以类似 RPC 的方式调用 web service JAX—RPC 的客户端编程模式有以下的三种 : Static stub( 静态的客户端存根调用 ) Dynamic proxy (部分动态的代理调用) Dynamic invocation interface (DII) (动态调用接口)
Web 服务 43/66 高级软件工程 三种 JAX—RPC 的客户端编程模式
Web 服务 44/66 高级软件工程 Static stub 1) 首先通过映射转换将服务描述的 WSDL 文档生成客户端的 Java stub 2) 然后实例化服务的 locator 实例 3) 通过 loacator 获得服务在客户端的代理 4) 用客户端代理 去调用服务
Web 服务 45/66 高级软件工程 package itso.test; import java.io.*; import java.util.*; import itso.test.*; public class WeatherForecastClient { public static void main(String [] args) { try{ WeatherForecastServiceLocator wsl = new WeatherForecastServiceLocator(); WeatherForecastService ws = (WeatherForecastService) wsl.getWeather(); String temperature = ws.getTemperature(); System.out.println(temperature); System.out.println("WeatherForecastClient completed"); } catch (Exception e) { e.printStackTrace(); }
Web 服务 46/66 高级软件工程 Dynamic proxy 与 Static stub 不同的是可以指定生成的客户端代理 import javax.xml.namespace.QName; import java.io.*; import java.util.*; public class WeatherForecastDynamicProxyClient { public static void main(String [] args){ try{ WeatherForecastServiceLocator wsl = new WeatherForecastServiceLocator(); QName qn = new QName(" "WeatherForecast"); WeatherForecast ws = (WeatherForecast) wsl.getPort(qn,WeatherForecast.class); String temperature = ws.getTemperature(); System.out.println(temperature); System.out.println("DynamicProxyJavaClient completed"); } catch (Exception e){ e.printStackTrace(); } }
Web 服务 47/66 高级软件工程 一个汽车网站的例子 例子构成: – JSPs – Controller Servlet – Stateless Session Bean – InventoryFacadeBean – Hibernate DAOs 暴露对象: InventoryFacadeBean.findAllAvailableCars()
Web 服务 48/66 高级软件工程 服务端点接口 将业务方法暴露为 Web Services 类似于服务器端的 stub 类似于 EJB Remote Interface package com.jbossatwork.ws; /** * Service endpoint interface for InventoryFacade. */ public interface InventoryEndpoint extends java.rmi.Remote{ public com.jbossatwork.ws.CarDTOArray findAvailableCars() throws java.rmi.RemoteException; } // InventoryEndpoint.java
Web 服务 49/66 高级软件工程 修改 ejb-jar.xml … … InventoryFacadeSB InventoryFacade … com.jbossatwork.ws.InventoryEndpoint … …
Web 服务 50/66 高级软件工程 webservices.xml 定义并注册 InventoryService Web Service 将 Service Endpoint Interface class 绑定到 InventoryFacadeBean EJB 告诉 J2EE app server 到哪里找 WSDL 与 JAX- RPC 映射文件( Mapping files , in EJB JAR file )
Web 服务 51/66 高级软件工程 <webservices xmlns=" xmlns:xsi=" xsi:schemaLocation=" version="1.1"> InventoryService META-INF/wsdl/InventoryService.wsdl META-INF/inventory-mapping.xml Inventory InventoryEndpointPort com.jbossatwork.ws.InventoryEndpoint InventoryFacade
Web 服务 52/66 高级软件工程 JAX-RPC 映射文件 帮助 JAX-RPC 编译器将 Java 对象映射到 WSDL 对象 复杂的 Java 对象导致复杂的 JAX-RPC 与 WSDL 文件
Web 服务 53/66 高级软件工程 <java-wsdl-mapping xmlns=" xmlns:xsi=" xsi:schemaLocation=" version="1.1"> com.jbossatwork.ws com.jbossatwork.ws com.jbossatwork.dto.CarDTOArray typeNS:CarDTOArray complexType cars
Web 服务 54/66 高级软件工程 com.jbossatwork.dto.CarDTO typeNS:CarDTO complexType id make model modelYear
Web 服务 55/66 高级软件工程 status com.jbossatwork.ws.InventoryService serviceNS:InventoryService InventoryEndpointPort
Web 服务 56/66 高级软件工程 com.jbossatwork.ws.InventoryEndpoint portTypeNS:InventoryEndpoint bindingNS:InventoryEndpointBinding findAvailableCars com.jbossatwork.dto.CarDTOArray wsdlMsgNS:InventoryEndpoint_findAvailableCarsResponse result
Web 服务 57/66 高级软件工程 <definitions name="InventoryService" targetNamespace=" xmlns:tns=" xmlns=" xmlns:xsd=" xmlns:ns2=" xmlns:soap=" <schema targetNamespace=" xmlns:tns= xmlns:soap11-enc=" xmlns:xsi=" xmlns:wsdl=" xmlns=" <element name="cars" type="tns:CarDTO" nillable="true" minOccurs="0" maxOccurs="unbounded"/> WSDL File
Web 服务 58/66 高级软件工程 <element name="status" type="string" nillable="true"/>
Web 服务 59/66 高级软件工程
Web 服务 60/66 高级软件工程 <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 4.0//EN" " … InventoryFacade Inventory jbossatwork-ws/InventoryService … … … 修改 WSDL URL
Web 服务 61/66 高级软件工程 修改 EJB /** * name="InventoryFacade" * … * view-type="all" * … * * name="Inventory" * wsdl-port="InventoryEndpointPort" * service-endpoint-interface="com.jbossatwork.ws.InventoryEndpoint" * service-endpoint-bean="com.jbossatwork.ejb.InventoryFacadeBean" * * service-endpoint-class="com.jbossatwork.ws.InventoryEndpoint" * */ public class InventoryFacadeBean implements SessionBean { …
Web 服务 62/66 高级软件工程 /** * view-type="all" * … * */ public CarDTOArray findAvailableCars() throws EJBException { CarDTOArray carDTOArray = new CarDTOArray(); CarDTO[] cars = (CarDTO[]) listAvailableCars().toArray(new CarDTO[0]); carDTOArray.setCars(cars); return carDTOArray; } /** * view-type="both" * … * */ public List listAvailableCars() throws EJBException { … } … }
Web 服务 63/66 高级软件工程 Web Services 与 Collections package com.jbossatwork.dto; import java.io.Serializable; import com.jbossatwork.dto.CarDTO; public class CarDTOArray implements Serializable { private CarDTO[] cars; public CarDTOArray() {} public CarDTO[] getCars() { return cars; } public void setCars(CarDTO[] cars) { this.cars = cars; } WSDL/XSD 不懂 Java Collections
Web 服务 64/66 高级软件工程 EJB JAR 文件结构 META-INF/ – ejb-jar.xml – jboss.xml – webservices.xml – inventory-mapping.xml (JAX-RPC Mapping File) – wsdl/ InventoryService.wsdl com/jbossatwork/ws/ – InventoryEndpoint.class
Web 服务 65/66 高级软件工程 客户端:产生 Web Service Proxy 代码 … … <wsdl2java output="${gen.source.dir}" url="InventoryService.wsdl" verbose="true"> <mapping namespace=" package="com.jbossatwork.client"/> <mapping namespace=" package="com.jbossatwork.client"/>
Web 服务 66/66 高级软件工程 package com.jbossatwork.client; public class MyAxisClient { public static void main(String [] args) { try { System.out.println("Finding InventoryService...\n"); InventoryService service = new InventoryServiceLocator(); System.out.println("Getting InventoryEndpoint...\n"); InventoryEndpoint endpoint=service.getInventoryEndpointPort(); System.out.println("Getting Cars..."); CarDTOArray carDTOArray = endpoint.findAvailableCars(); CarDTO[] cars = carDTOArray.getCars(); for (int i = 0; i < cars.length; ++i) { System.out.println( "Year = [" + cars[i].getModelYear() + "], Make = [" + cars[i].getMake() + "], Model = [" + ars[i].getModel() + "], status = [" + cars[i].getStatus() + "]"); } } catch(Exception e) { e.printStackTrace(); } 客户代码( J2SE 1.4 )