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
