Web Service学习笔记(webservice、soap、wsdl、jws详细分析)
Web Service的定义
W3C组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计。Web Service服务通常被定义为一组模块化的API,它们可以通过网络进行调用,来执行远程系统的请求服务。
这 里我们从一个程序员的视角来观察web service。在传统的程序编码中,存在这各种的函数方法调用。通常,我们知道一个程序模块M中的方法A,向其发出调用请求,并传入A方法需要的参数 P,方法A执行完毕后,返回处理结果R。这种函数或方法调用通常发生在同一台机器上的同一程序语言环境下。现在的我们需要一种能够在不同计算机间的不同语 言编写的应用程序系统中,通过网络通讯实现函数和方法调用的能力,而Web service正是应这种需求而诞生的。
最 普遍的一种说法就是,Web Service = SOAP + HTTP + WSDL。其中,SOAP Simple Object Access Protocol)协议是web service的主体,它通过HTTP或者SMTP等应用层协议进行通讯,自身使用XML文件来描述程序的函数方法和参数信息,从而完成不同主机的异构系 统间的计算服务处理。这里的WSDL(Web Services Description Language)web 服务描述语言也是一个XML文档,它通过HTTP向公众发布,公告客户端程序关于某个具体的 Web service服务的URL信息、方法的命名,参数,返回值等。
下面,我们先来熟悉一下SOAP协议,看看它是如何描述程序中的函数方法、参数及结果对象的。
SOAP协议简介
什么是SOAP
SOAP 指简单对象访问协议,它是一种基于XML的消息通讯格式,用于网络上,不同平台,不同语言的应用程序间的通讯。可自定义,易于扩展。一条 SOAP 消息就是一个普通的 XML 文档,包含下列元素:
• Envelope 元素,标识XML 文档一条 SOAP 消息
• Header 元素,包含头部信息的XML标签
• Body 元素,包含所有的调用和响应的主体信息的标签
• Fault 元素,错误信息标签。
以上的元素都在 SOAP的命名空间http://www.w3.org/2001/12/soap-envelope中声明;
SOAP的语法规则
• SOAP 消息必须用 XML 来编码
• SOAP 消息必须使用 SOAP Envelope 命名空间
• SOAP 消息必须使用 SOAP Encoding 命名空间
• SOAP 消息不能包含 DTD 引用
• SOAP 消息不能包含 XML 处理指令
SOAP 消息的基本结构
- ?>
- <soap:Envelope
- xmlns:soap=
- soap:encodingStyle=>
- <soap:Header>
- ...
- ...
- </soap:Header>
- <soap:Body>
- ...
- ...
- <soap:Fault>
- ...
- ...
- </soap:Fault>
- </soap:Body>
- </soap:Envelope>
SOAP Envelope 元素
Envelope 元素是 SOAP 消息的根元素。它指明 XML 文档是一个SOAP 消息。它的属性 xmlns:soap的值必须是http://www.w3.org/2001/12/soap-envelope。
encodingStyle 属性,语法:soap:encodingStyle="URI"
encodingStyle 属性用于定义文档中使用的数据类型。此属性可出现在任何 SOAP 元素中,并会被应用到元素的内容及元素的所有子元素上。
- ?>
- <soap:Envelope
- xmlns:soap=
- soap:encodingStyle=>
- ...
- Message information goes here
- ...
- </soap:Envelope>
SOAP Header 元素
- actor 属性,语法soap:actor="URI"
通 过沿着消息路径经过不同的端点,SOAP 消息可从某个发送者传播到某个接收者。并非 SOAP 消息的所有部分均打算传送到 SOAP 消息的最终端点,不过,另一个方面,也许打算传送给消息路径上的一个或多个端点。SOAP 的 actor 属性可被用于将 Header 元素寻址到一个特定的端点。
- mustUnderstand 属性 ,语法soap:mustUnderstand="0|1"
SOAP 的 mustUnderstand 属性可用于标识标题项对于要对其进行处理的接收者来说是强制的还是可选的。假如您向 Header 元素的某个子元素添加了 "mustUnderstand="1",则要求处理此头部的接收者必须认可此元素。
- <? xml version=?>
- <soap:Envelope
- "http://www.w3.org/2001/12/soap-envelope"
- soap:encodingStyle=>
- <soap:Header>
- <m:Trans
- "http://www.jsoso.net/transaction/"
- soap:mustUnderstand=
- //www.w3schools.com/appml/ “ >234</m:Trans>
- </soap:Header>
- ...
- ...
- </soap:Envelope>
- <? xml version=?>
- <soap:Envelope
- "http://www.w3.org/2001/12/soap-envelope"
- soap:encodingStyle=>
- <soap:Body>
- <m:GetPrice xmlns:m=>
- <m:Item>Apples</m:Item>
- </m:GetPrice>
- </soap:Body>
- </soap:Envelope>
上面的例子请求苹果的价格。请注意,上面的 m:GetPrice 和 Item 元素是应用程序专用的元素。它们并不是 SOAP 标准的一部分。而对应的 SOAP 响应应该类似这样:
- ?>
- <soap:Envelope
- xmlns:soap=
- soap:encodingStyle=>
- <soap:Body>
- <m:GetPriceResponse xmlns:m=>
- <m:Price></m:Price>
- </m:GetPriceResponse>
- </soap:Body>
- </soap:Envelope>
SOAP Fault 元素
Fault 元素表示 SOAP的错误消息。它必须是 Body 元素的子元素,且在一条 SOAP 消息中,Fault 元素只能出现一次。Fault 元素拥有下列子元素:
常用的SOAP Fault Codes
HTTP协议中的SOAP 实例
下面的例子中,一个 GetStockPrice 请求被发送到了服务器。此请求有一个 StockName 参数,而在响应中则会返回一个 Price 参数。此功能的命名空间被定义在此地址中: "http://www.jsoso.net/stock"
- SOAP 请求:(注意HTTP的Head属性)
- 1.1
- Host: www.jsoso.net
- Content-Type: application/soap+xml; charset=utf-
- Content-Length: XXX
- <? xml version=?>
- <soap:Envelope
- xmlns:soap=
- soap:encodingStyle=>
- <soap:Body xmlns:m=>
- <m:GetStockPrice>
- <m:StockName>IBM</m:StockName>
- </m:GetStockPrice>
- </soap:Body>
- </soap:Envelope>
- SOAP 响应:(注意HTTP的Head属性)
- 1.1200 OK
- Content-Type: application/soap+xml; charset=utf-
- Content-Length: XXX
- <? xml version=?>
- <soap:Envelope
- xmlns:soap=
- soap:encodingStyle=>
- <soap:Body xmlns:m=>
- <m:GetStockPriceResponse>
- <m:Price></m:Price>
- </m:GetStockPriceResponse>
- </soap:Body>
- </soap:Envelope>
HTTP协议中的SOAP RPC工作流程
WSDL简介
介绍过了SOAP,让我们关注Web Service中另外一个重要的组成WSDL。
WSDL的主要文档元素
WSDL
文档可以分为两部分。顶部分由抽象定义组成,而底部分则由具体描述组成。抽象部分以独立于平台和语言的方式定义SOAP消息,它们并不包含任何随机器或语
言而变的元素。这就定义了一系列服务,截然不同的应用都可以实现。具体部分,如数据的序列化则归入底部分,因为它包含具体的定义。在上述的文档元素
中,<types>、<message>、<portType>属于抽象定义
层,<binding>、<service>属于具体定义层。所有的抽象可以是单独存在于别的文件中,也可以从主文档中导入。
WSDL文档的结构实例解析
下面我们将通过一个实际的WSDL文档例子来详细说明各标签的作用及关系。
- encoding=?>
- <definitions
- xmlns:soap=
- xmlns:tns=
- xmlns:xsd=
- xmlns=
- targetNamespace=
- name=>
- <types>
- <xsd:schema>
- <xsd:
- namespace=
- schemaLocation=></xsd:>
- </xsd:schema>
- </types>
- <message name=>
- <part name= type=></part>
- </message>
- <message name=>
- <part name= type=></part>
- </message>
- <message name=>
- <part name= type=></part>
- <part name= type=></part>
- </message>
- <message name=>
- <part name= type=></part>
- </message>
- <message name=>
- <part name= element=></part>
- </message>
- <portType name=>
- <operation name= parameterOrder=>
- <input message=></input>
- <output message=></output>
- </operation>
- <operation name= parameterOrder=>
- <input message=></input>
- <output message=></output>
- <fault message= name=></fault>
- </operation>
- </portType>
- <binding name= type=>
- <soap:binding
- transport=
- style=></soap:binding>
- <operation name=>
- <soap:operation soapAction=></soap:operation>
- <input>
- <soap:body use=
- namespace=></soap:body>
- </input>
- <output>
- <soap:body use=
- namespace=></soap:body>
- </output>
- </operation>
- <operation name=>
- <soap:operation soapAction=></soap:operation>
- <input>
- <soap:body use=
- namespace=></soap:body>
- </input>
- <output>
- <soap:body use=
- namespace=></soap:body>
- </output>
- <fault name=>
- <soap:fault name= use=></soap:fault>
- </fault>
- </operation>
- </binding>
- <service name=>
- <port name= binding=>
- <soap:address location=></soap:address>
- </port>
- </service>
- </definitions>
由于上面的事例XML较长,我们将其逐段分解讲解
WSDL文档的根元素:<definitions>
- <definitions
- xmlns:soap=
- xmlns:tns=
- xmlns:xsd=
- xmlns=
- targetNamespace=
- name=>
- ……
- ……
- </definitions>
<definitions>
定义了文档中用到的各个xml元素的namespace缩写,也界定了本文档自己的
targetNamespace="http://www.jsoso.com/wstest",这意味着其它的XML要引用当前XML中的元素时,要声
明这个namespace。注意xmlns:tns="http://www.jsoso.com/wstest"这个声明,它标示了使用tns这个前缀
指向自身的命名空间。
引用WSDL文档数据类型定义元素:<types>
- <types>
- <xsd:schema>
- <xsd:
- namespace=
- schemaLocation=></xsd:>
- </xsd:schema>
- </types>
<types> 标签定义了当前的WSDL文档用到的数据类型。要说明的是,为了最大程度的平台中立性,WSDL 使用 XML Schema 语法来定义数据类型。这些数据类型用来定义web service方法的参数和返回指。对于通用的原生数据类型如:integer , boolean , char , float等,在W3C的标准文档http://www.w3.org/2001/XMLSchema中已经做了定义。这里我们要引入的schema定义 schemaLocation="http://localhost:8080/hello?xsd=1"是我们自定义的Java对象类型。
WSDL文档消息体定义元素:< message >
- >
- <part name= type=></part>
- </message>
- <message name=>
- <part name= type=></part>
- </message>
- <message name=>
- <part name= type=></part>
- <part name= type=></part>
- </message>
- <message name=>
- <part name= type=></part>
- </message>
- <message name=>
- <part name= element=></part>
- </message>
<message> 元素定义了web service函数的参数。<message>元素中的每个<part>子元素都和某个参数相符。输入参数在<message>元素中定义,与输出参数相 隔离,输出参数有自己的<message>元素。兼作输入、输出的参数在输入输出的<message>元素中有它们相应的<part>元素。输出 <message>元素以"Response"结尾,对Java而言方法得返回值就对应一个输出的<message>。每个<part>元素都有名字和类 型属性,就像函数的参数有参数名和参数类型。
在上面的文档中有两个输入参数、两个输出参数和一个错误参数(对应Java中的Exception)。
输入参数<message>的name属性分别命名为toSayHello,sayHello。
toSayHello对应输入参数userName,参数类型为xsd:string,在Java语言中就是String;
sayHello对应两个输入参数person和arg1,类型为tns:person和xsd:string。这里tns:person类型就是引用了< types >标签中的类型定义。
输出参数<message>的name属性分别命名为toSayHelloResponse和sayHelloResponse。
这个名称和输入参数的<message>标签name属性对应,在其后面加上Response尾缀。
toSayHelloResponse对应的返回值是returnWord,参数类型为xsd:string;
sayHelloResponse对应的返回值是personList,参数类型为tns:personArray(自定义类型);
错误参数<message>的name属性为HelloException。
它的<part>子标签element而不是type来定义类型。
以 上的message标签的name属性通常使用web service函数方法名作为参照,错误参数标签则使用异常类名为参照。标签中的参数名称,即part子元素的name属性是可自定义的(下一章节详细说 明)。message标签的参数类型将引用types标签的定义。
WSDL文档函数体定义元素:< portType >- <portType name=>
- <operation name= parameterOrder=>
- ></input>
- <output message=></output>
- </operation>
- <operation name= parameterOrder=>
- ></input>
- <output message=></output>
- name=></fault>
- </operation>
- </portType>
<portType> 元素是最重要的 WSDL 元素。它可描述一个 web service、可被执行的操作,以及相关的消息。portType的name属性对应Java中的一个服务类的类名。<portType> 元素使用其子元素< operation>描述一个web service的服务方法。
在<operation> 元素中,name属性表示服务方法名,parameterOrder属性表示方法的参数顺序,使用空格符分割多个参数, 如:“parameterOrder="person arg1”。<operation>元素的子标签<input>表示输入参数说明,它引用<message>标签中的输入参 数。<output>表示输出参数说明,它引用<message>标签中的输出参数。<fault>标签在Java方法中的特别 用来表示异常(其它语言有对应的错误处理机制),它引用<message>标签中的错误参数。
WSDL绑定实现定义元素:< binding >- <binding name= type=>
- <soap:binding
- style=></soap:binding>
- >
- <soap:operation soapAction=></soap:operation>
- <input>
- <soap:body use=
- ></soap:body>
- </input>
- <output>
- <soap:body use=
- ></soap:body>
- </output>
- </operation>
- <operation name=>
- ></soap:operation>
- <input>
- namespace=></soap:body>
- </input>
- <output>
- namespace=></soap:body>
- </output>
- <fault name=>
- use=></soap:fault>
- </fault>
- </operation>
- </binding>
- <service name=>
- <port name= binding=>
- ></soap:address>
- </port>
- </service>
service是一套<port>元素。在一一对应形式下,每个<port>元素都和一个location关联。如果同一个<binding>有多个<port>元素与之关联,可以使用额外的URL地址作为替换。
一
个WSDL文档中可以有多个<service>元素,而且多个<service>元素十分有用,其中之一就是可以根据目标URL来组织端口。在一个
WSDL文档中,<service>的name属性用来区分不同的service。在同一个service中,不同端口,使用端口的"name"属性区
分。
这一章节,我们简单的描述了WSDL对SOAP协议的支持,以及在Web Service中的作用。在接下来的章节中,我们将学习如何使用Java6.0的Annotation标签来定义和生成对应的WSDL。
JavaSE6.0下的Web Service
从JavaSE6.0开始,Java引入了对Web Service的原生支持。我们只需要简单的使用Java的Annotation标签即可将标准的Java方法发布成Web Service。(PS:Java Annotation资料请参考
JDK5.0 Annotation学习笔记(一) )
但不是所有的Java类都可以发布成Web Service。Java类若要成为一个实现了Web Service的bean,它需要遵循下边这些原则:
- 这个类必须是public类
- 这些类不能是final的或者abstract
- 这个类必须有一个公共的默认构造函数
- 这个类绝对不能有finalize()方法
下面我们将通过一个具体的Java Web Service代码例子,配合上述的WSDL文件,讲述如何编写JavaSE6.0的原生Web Service应用。
完整的Java Web Service类代码
- package
- import import import import import import import /
- * 提供WebService服务的类
- */
- @WebService"Example""http://www.jsoso.com/wstest""Example")
- (style=SOAPBinding.Style.RPC)
- publicclass Example {
- ArrayList<Person> persons = ArrayList<Person>();;
- *
- * 返回一个字符串
- * @param userName
- * @return
- (operationName=,action=,exclude=)
- (name=)
- String sayHello((name=)String userName) {
- + userName;
- }
- * web services 方法的返回值与参数的类型不能为接口
- * @param person
- * @return
- * @throws HelloException
- (operationName=, action=)
- (partName=)
- Person[] sayHello((partName=, mode=Mode.IN)Person person,
- String userName) HelloException {
- (person == || person.getName() == ) {
- HelloException();
- }
- System.out.println(person.getName() + + userName + + person.getAge() + );
- persons.add(person);
- persons.toArray( Person[]);
- }
- }
- package
- import import import import import
- /**
- */
- StartServer {
- * @param args
- main(String[] args) {
- * 生成Example 服务实例
- Example serverBean = Example();
- * 发布Web Service到http://localhost:8080/hello地址
- Endpoint endpoint =
- Endpoint.publish(, serverBean);
- Binding binding = endpoint.getBinding();
- * 设置一个SOAP协议处理栈
- * 这里就简单得打印SOAP的消息文本
- */
- List<Handler> handlerChain = LinkedList<Handler>();
- TraceHandler());
- binding.setHandlerChain(handlerChain);
- );
- }
- }
- package
- import
- publicclass RunClient {
- * @param args
- */
- main(String[] args) {
- Example_Service service = Example_Service();
- Example server = (Example) service.getExamplePort();
- {
- + server.toSayHello());
- Person person = Person();
- );
- person.setAge();
- server.sayHello(person, );
- person = Person();
- );
- person.setAge();
- PersonArray list = server.sayHello(person, );
- System.out.println();
- (Person p : list.getItem()) {
- System.out.println(p.getName() + + p.getAge());
- }
- } (HelloException_Exception e) {
- e.printStackTrace();
- }
- }
- }
- -cp 定义classpath
- -r 生成 bean的wsdl文件的存放目录
- -s 生成发布Web Service的源代码文件的存放目录(如果方法有抛出异常,则会生成该异常的描述类源文件)
- -d 生成发布Web Service的编译过的二进制类文件的存放目录(该异常的描述类的class文件)
- -d 生成客户端执行类的class文件的存放目录
- -s 生成客户端执行类的源文件的存放目录
- -p 定义生成类的包名
命令范例:wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref http://localhost:8080/hello?wsdl
--转自 http://blog.csdn.net/lfy9608110935/article/details/8302306