Web Service概述

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 消息的基本結構
Java代碼
<??xml?version="1.0"?>???
<soap:Envelope???
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"??
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">???
<soap:Header>???
?????
?????
</soap:Header>???
<soap:Body>???
?????
?????
??
<soap:Fault>???
???????
???????
??
</soap:Fault>???
</soap:Body>???
</soap:Envelope>??
<??xml?version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">
<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 元素中,并會被應用到元素的內容及元素的所有子元素上。
Java代碼
<??xml?version="1.0"?>???
<soap:Envelope???
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"??
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">???
?????
??Message?information?goes?here???
?????
</soap:Envelope>??
<??xml?version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">
??
??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",則要求處理此頭部的接收者必須認可此元素。
Java代碼
<??xml?version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">
<soap:Header>
<m:Trans
xmlns:m="http://www.jsoso.net/transaction/"?
soap:mustUnderstand
="1"?
soap:actor
="http://www.w3schools.com/appml/?“??>234</m:Trans>
</soap:Header>


</soap:Envelope>


SOAP Body 元素
必需的 SOAP Body 元素可包含打算傳送到消息最終端點的實際 SOAP 消息。Body元素中既可以包含SOAP定義的命名空間中的元素,如Fault,也可以是用戶的應用程序自定義的元素。以下是一個用戶定義的請求:
Java代碼
<??xml?version="1.0"?>???
<soap:Envelope???
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"??
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">???
<soap:Body>???
???
<m:GetPrice?xmlns:m="http://www.jsoso.net/prices">???
??????
<m:Item>Apples</m:Item>???
???
</m:GetPrice>???
</soap:Body>???
</soap:Envelope>??

上面的例子請求蘋果的價格。請注意,上面的 m:GetPrice 和 Item 元素是應用程序專用的元素。它們并不是 SOAP 標準的一部分。而對應的 SOAP 響應應該類似這樣:
Java代碼
<?xml?version="1.0"?>???
<soap:Envelope???
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"??
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">???
<soap:Body>???
???
<m:GetPriceResponse?xmlns:m="http://www.jsoso.net/prices">???
??????
<m:Price>1.90</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屬性)

Java代碼
POST?/InStock?HTTP/1.1??
Host:?www.jsoso.net???
Content-Type:?application/soap+xml;?charset=utf-8??
Content-Length:?XXX???
??
<??xml?version="1.0"?>???
<soap:Envelope???
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"??
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">???
??
<soap:Body?xmlns:m="http://www.jsoso.net/stock">???
????
<m:GetStockPrice>???
??????
<m:StockName>IBM</m:StockName>???
????
</m:GetStockPrice>???
??
</soap:Body>?????
</soap:Envelope>??


  • SOAP 響應:(注意HTTP的Head屬性)

Java代碼
HTTP/1.1?200?OK???
Content-Type:?application/soap+xml;?charset=utf-8??
Content-Length:?XXX???
??
<??xml?version="1.0"?>???
<soap:Envelope???
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"??
soap:encodingStyle
="http://www.w3.org/2001/12/soap-encoding">???
??
<soap:Body?xmlns:m="http://www.jsoso.net/stock">???
????
<m:GetStockPriceResponse>???
??????
<m:Price>34.5</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文檔例子來詳細說明各標簽的作用及關系。
Java代碼

<?xml?version="1.0"?encoding="UTF-8"?>???
<definitions???
?
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"??
?xmlns:tns
="http://www.jsoso.com/wstest"??
?xmlns:xsd
="http://www.w3.org/2001/XMLSchema"??
?xmlns
="http://schemas.xmlsoap.org/wsdl/"??
?targetNamespace
="http://www.jsoso.com/wstest"??
?name
="Example">???
??
<types>???
??
<xsd:schema>???
??
<xsd:import??
???
namespace="http://www.jsoso.com/wstest"??
???schemaLocation
="http://localhost:8080/hello?xsd=1"></xsd:import>???
??
</xsd:schema>???
</types>???
??
<message?name="toSayHello">???
??
<part?name="userName"?type="xsd:string"></part>???
</message>???
<message?name="toSayHelloResponse">???
??
<part?name="returnWord"?type="xsd:string"></part>???
</message>???
??
<message?name="sayHello">???
??
<part?name="person"?type="tns:person"></part>???
??
<part?name="arg1"?type="xsd:string"></part>???
</message>???
<message?name="sayHelloResponse">???
??
<part?name="personList"?type="tns:personArray"></part>???
</message>???
<message?name="HelloException">???
??
<part?name="fault"?element="tns:HelloException"></part>???
</message>???
??
<portType?name="Example">???
??
<operation?name="toSayHello"?parameterOrder="userName">???
????
<input?message="tns:toSayHello"></input>???
????
<output?message="tns:toSayHelloResponse"></output>???
??
</operation>???
??
<operation?name="sayHello"?parameterOrder="person?arg1">???
????
<input?message="tns:sayHello"></input>???
????
<output?message="tns:sayHelloResponse"></output>???
????
<fault?message="tns:HelloException"?name="HelloException"></fault>???
??
</operation>???
</portType>???
??
<binding?name="ExamplePortBinding"?type="tns:Example">???
??
<soap:binding???
????
transport="http://schemas.xmlsoap.org/soap/http"????
????style
="rpc"></soap:binding>???
??
<operation?name="toSayHello">???
????
<soap:operation?soapAction="sayHello"></soap:operation>???
????
<input>???
??????
<soap:body?use="literal"??
????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</input>???
????
<output>???
??????
<soap:body?use="literal"??
?????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</output>???
??
</operation>???
??
<operation?name="sayHello">???
????
<soap:operation?soapAction="sayHello"></soap:operation>???
????
<input>???
??????
<soap:body?use="literal"??
????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</input>???
????
<output>???
??????
<soap:body?use="literal"??
????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</output>???
????
<fault?name="HelloException">???
??????
<soap:fault?name="HelloException"?use="literal"></soap:fault>???
????
</fault>???
????
</operation>???
</binding>???
??
<service?name="Example">???
??
<port?name="ExamplePort"?binding="tns:ExamplePortBinding">???
????
<soap:address?location="http://localhost:8080/hello"></soap:address>???
??
</port>???
</service>???
</definitions>??

由于上面的事例XML較長,我們將其逐段分解講解

WSDL文檔的根元素:<definitions>
Java代碼

<definitions???
?
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"??
?xmlns:tns
="http://www.jsoso.com/wstest"??
?xmlns:xsd
="http://www.w3.org/2001/XMLSchema"??
?xmlns
="http://schemas.xmlsoap.org/wsdl/"??
?targetNamespace
="http://www.jsoso.com/wstest"??
?name
="Example">???
……???
……???
</definitions>??

<definitions>定義了文檔中用到的各個xml元素的namespace縮寫,也界定了本文檔自己的targetNamespace="http://www.jsoso.com/wstest",這意味著其它的XML要引用當前XML中的元素時,要聲明這個namespace。注意xmlns:tns="http://www.jsoso.com/wstest"這個聲明,它標示了使用tns這個前綴指向自身的命名空間。

引用
WSDL文檔數據類型定義元素:<types>

Java代碼

<types>???
??
<xsd:schema>???
??
<xsd:import??
???
namespace="http://www.jsoso.com/wstest"??
???schemaLocation
="http://localhost:8080/hello?xsd=1"></xsd:import>???
??
</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 >
Java代碼

<message?name="toSayHello">???
??
<part?name="userName"?type="xsd:string"></part>???
</message>???
<message?name="toSayHelloResponse">???
??
<part?name="returnWord"?type="xsd:string"></part>???
</message>???
??
<message?name="sayHello">???
??
<part?name="person"?type="tns:person"></part>???
??
<part?name="arg1"?type="xsd:string"></part>???
</message>???
<message?name="sayHelloResponse">???
??
<part?name="personList"?type="tns:personArray"></part>???
</message>???
<message?name="HelloException">???
??
<part?name="fault"?element="tns:HelloException"></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 >
Java代碼

<portType?name="Example">???
??
<operation?name="toSayHello"?parameterOrder="userName">???
????
<input?message="tns:toSayHello"></input>???
????
<output?message="tns:toSayHelloResponse"></output>???
??
</operation>???
??
<operation?name="sayHello"?parameterOrder="person?arg1">???
????
<input?message="tns:sayHello"></input>???
????
<output?message="tns:sayHelloResponse"></output>???
????
<fault?message="tns:HelloException"?name="HelloException"></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 >
Java代碼

<binding?name="ExamplePortBinding"?type="tns:Example">???
??
<soap:binding???
????
transport="http://schemas.xmlsoap.org/soap/http"????
????style
="rpc"></soap:binding>???
??
<operation?name="toSayHello">???
????
<soap:operation?soapAction="sayHello"></soap:operation>???
????
<input>???
??????
<soap:body?use="literal"??
????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</input>???
????
<output>???
??????
<soap:body?use="literal"??
?????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</output>???
??
</operation>???
??
<operation?name="sayHello">???
????
<soap:operation?soapAction="sayHello"></soap:operation>???
????
<input>???
??????
<soap:body?use="literal"??
????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</input>???
????
<output>???
??????
<soap:body?use="literal"??
????????namespace
="http://www.jsoso.com/wstest"></soap:body>???
????
</output>???
????
<fault?name="HelloException">???
??????
<soap:fault?name="HelloException"?use="literal"></soap:fault>???
????
</fault>???
????
</operation>???
</binding>??

<binding>標簽是完整描述協議、序列化和編碼的地方,<types>,<message>和<portType>標簽處理抽象的數據內容,而<binding>標簽是處理數據傳輸的物理實現。
<binding>標簽把前三部分的抽象定義具體化。

首先<binding>標簽使用<soap:binding>的transport和style屬性定義了Web Service的通訊協議HTTP和SOAP的請求風格RPC。其次<operation>子標簽將portType中定義的operation同SOAP的請求綁定,定義了操作名稱soapAction,輸出輸入參數和異常的編碼方式及命名空間。

WSDL服務地址綁定元素:< service >
Java代碼

<service?name="Example">???
??
<port?name="ExamplePort"?binding="tns:ExamplePortBinding">???
????
<soap:address?location="http://localhost:8080/hello"></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類代碼

Java代碼
package?org.jsoso.jws.server;

import?java.util.ArrayList;
import?javax.jws.WebMethod;
import?javax.jws.WebParam;
import?javax.jws.WebResult;
import?javax.jws.WebService;
import?javax.jws.WebParam.Mode;
import?javax.jws.soap.SOAPBinding;
/
?
*?提供WebService服務的類
?
*/
@WebService(name
="Example",?targetNamespace="http://www.jsoso.com/wstest",?serviceName="Example")
@SOAPBinding(style
=SOAPBinding.Style.RPC)
public?class?Example?{
????
private?ArrayList<Person>?persons?=?new?ArrayList<Person>();;
????
/**
?????*?
?????*?返回一個字符串
?????*?
@param?userName
?????*?
@return
?????
*/

????@WebMethod(operationName
="toSayHello",action="sayHello",exclude=false)
????@WebResult(name
="returnWord")//自定義該方法返回值在WSDL中相關的描述
????public?String?sayHello(@WebParam(name="userName")String?userName)?{
????????
return?"Hello:"?+?userName;
????}


????
/**
?????*?web?services?方法的返回值與參數的類型不能為接口
?????*?
@param?person
?????*?
@return
?????*?
@throws?HelloException
?????
*/

????@WebMethod(operationName
="sayHello",?action="sayHello")
????@WebResult(partName
="personList")
????
public?Person[]?sayHello(@WebParam(partName="person",?mode=Mode.IN)Person?person,?
????????????String?userName)?
throws?HelloException?{
????????
if?(person?==?null?||?person.getName()?==?null)?{
????????????
throw?new?HelloException("說hello出錯,對像為空。。");
????????}

????????System.out.println(person.getName()?
+?"?對?"?+?userName?+?"?說:Hello,我今年"?+?person.getAge()?+?"");
????????persons.add(person);
????????
return?persons.toArray(new?Person[0]);
????}

}




Annotation 1@WebService(name="Example", targetNamespace="http://www.jsoso.com/wstest", serviceName="Example")
@WebService標簽主要將類暴露為WebService,其中targetNamespace屬性定義了自己的命名空間,serviceName則定義了< definitions >標簽和<service>標簽的name屬性。

Annotation 2:@SOAPBinding(style=SOAPBinding.Style.RPC)
@SOAPBinding標簽定義了WSDL文檔中SOAP的消息協議,其中style屬性對應SOAP的文檔類型,可選的有RPC和DOCUMENT

Annotation 3:@WebMethod(operationName="toSayHello",action="sayHello",exclude=false)
@WebMethod定義Web Service運作的方法,
屬性action 對應操作的活動 ,如<soap:operation soapAction="sayHello" />
屬性operationName匹配的wsdl:operation 的名稱,如<operation name="toSayHello" parameterOrder="userName">
屬性exclude 用于阻止將某一繼承方法公開為web服務,默認為false

Annotation 4:@WebResult(name="returnWord")
@ WebResult定義方法返回值得名稱,如<part name="returnWord" type="xsd:string" />

Annotation 5:@WebParam(partName="person", mode=Mode.IN
@WebParam定義方法的參數名稱,如<part name="person" type="tns:person" />,其中mode屬性表示參數的流向,可選值有IN / OUT / INOUT

這里要著重說明的是,上述Web Service類的sayHello方法中,帶有HelloException這個異常聲明,造成該服務類不能直接發布成Web Service。需要使用wsgen工具為其生存異常Bean。關于wsgen工具的使用,請參考wsgen與wsimport命令說明

發布一個的Java Web Service
在完成了上述的Web Service Annotation注釋后,我們使用wsgen工具為其進行服務資源文件的構造(這里主要是生成一個名為org.jsoso.jws.server.jaxws.HelloExceptionBean的異常bean類),最后使用以下的類發布Web 服務:
Java代碼

package?org.jsoso.jws.server;???
??
import?java.util.LinkedList;???
import?java.util.List;???
import?javax.xml.ws.Binding;???
import?javax.xml.ws.Endpoint;???
import?javax.xml.ws.handler.Handler;???
??
/**??
?*?
@author?zsy?啟動web?services服務??
?
*/
??
public?class?StartServer?{???
??
????
/**??
?????*?
@param?args??
?????
*/
??
????
public?static?void?main(String[]?args)?{???
????????
/*??
?????????*?生成Example?服務實例??
?????????
*/
??
????????Example?serverBean?
=?new?Example();???
????????
/*??
?????????*?發布Web?Service到http://localhost:8080/hello地址??
?????????
*/
??
????????Endpoint?endpoint?
=????
???????????Endpoint.publish(
"http://localhost:8080/hello",?serverBean);???
????????Binding?binding?
=?endpoint.getBinding();???
????????
/*??
?????????*?設置一個SOAP協議處理棧??
?????????*?這里就簡單得打印SOAP的消息文本??
?????????
*/
??
????????List
<Handler>?handlerChain?=?new?LinkedList<Handler>();???
????????handlerChain.add(
new?TraceHandler());???
????????binding.setHandlerChain(handlerChain);???
????????System.out.println(
"服務已啟動?http://localhost:8080/hello");???
????}
???
}
??

在控制臺運行這個類,就可以使用URL :http://localhost:8080/hello?wsdl 瀏覽到上文所描述的WSDL的全文了。這說明您的第一個Web Service應用發布成功!

構建Web Service客戶端
使用JavaSE6.0構建Web Service的客戶端是一件相當簡單的事。這里我們要使用到JDK中的另一個命令行工具wsimport。在控制臺下輸入以下命令:
引用
wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref? http://localhost:8080/hello?wsdl

即可在包org.jsoso.jws.client.ref中生成客戶端的存根及框架文件。其中我們要使用的類只有兩個:服務類Example_Service和本地接口Example。編寫如下客戶端,即可調用Web Service服務:
Java代碼

package?org.jsoso.jws.client;???
??
import?org.jsoso.jws.client.ref.*;???
??
public?class?RunClient?{???
??
????
/**??
?????*?
@param?args??
?????
*/
??
????
public?static?void?main(String[]?args)?{???
????????
//初始化服務框架類???
????????Example_Service?service?=?new?Example_Service();???
????????
//或者本地服務借口的實例???
????????Example?server?=?(Example)?service.getExamplePort();???
????????
try?{???
????????????
//調用web?service的toSayHello方法???
????????????System.out.println("輸入toSayHello的返回值——"?+?server.toSayHello("阿土"));????????????
?????????????Person?person?
=?new?Person();???
?????????????person.setName(
"阿土");???
?????????????person.setAge(
25);???
?????????????
//調用web?service的sayHello方法???
?????????????server.sayHello(person,?"機器人");???
????????????????
?????????????person?
=?new?Person();???
?????????????person.setName(
"aten");???
?????????????person.setAge(
30);???
?????????????
//調用web?service的sayHello方法???
?????????????PersonArray?list?=?server.sayHello(person,?"機器人");???
????????????
//輸出返回值???
?????????????System.out.println("\n以下輸入sayHello的返回值——");???
????????????
for?(Person?p?:?list.getItem())?{???
????????????????System.out.println(p.getName()?
+?":"?+?p.getAge());???
????????????}
??????????????
????????}
?catch?(HelloException_Exception?e)?{???
????????????e.printStackTrace();???
????????}
???
????}
???
}
??
屆此,本次Web Service的學習暫告一個段落。Java Web Service是一個相當龐大的知識體系,其中涉及的相關技術較多,這里無法一一道來,我們將會在今后的開發和使用中,同大家做進一步深入的探討和學習。

附錄:wsgen與wsimport命令說明

wsgen
wsgen是在JDK的bin目錄下的一個exe文件(Windows版),該命令的主要功能是用來生成合適的JAX-WS。它讀取Web Service的終端類文件,同時生成所有用于發布Web Service所依賴的源代碼文件和經過編譯過的二進制類文件。這里要特別說明的是,通常在Web Service Bean中用到的異常類會另外生成一個描述Bean,如果Web Service Bean中的方法有申明拋出異常,這一步是必需的,否則服務器無法綁定該對像。此外,wsgen還能輔助生成WSDL和相關的xsd文件。wsgen從資源文件生成一個完整的操作列表并驗證web service是否合法,可以完整發布。
命令參數說明:
  • ? -cp 定義classpath
  • ? -r 生成 bean的wsdl文件的存放目錄
  • ? -s 生成發布Web Service的源代碼文件的存放目錄(如果方法有拋出異常,則會生成該異常的描述類源文件)
  • ? -d 生成發布Web Service的編譯過的二進制類文件的存放目錄(該異常的描述類的class文件)

命令范例:wsgen -cp ./bin -r ./wsdl -s ./src -d ./bin -wsdl org.jsoso.jws.server.Example

wsimport
wsimport也是在JDK的bin目錄下的一個exe文件(Windows版),主要功能是根據服務端發布的wsdl文件生成客戶端存根及框架,負責與Web Service 服務器通信,并在將其封裝成實例,客戶端可以直接使用,就像使用本地實例一樣。對Java而言,wsimport幫助程序員生存調用web service所需要的客戶端類文件.java和.class。要提醒指出的是,wsimport可以用于非Java的服務器端,如:服務器端也許是C#編寫的web service,通過wsimport則生成Java的客戶端實現。
命令參數說明:
  • ? -d 生成客戶端執行類的class文件的存放目錄
  • ? -s 生成客戶端執行類的源文件的存放目錄
  • ? -p 定義生成類的包名

命令范例:wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref http://localhost:8080/hello?wsdl