Web Service学习笔记
生活随笔
收集整理的這篇文章主要介紹了
Web Service学习笔记
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Web Service概述
Web Service的定義
W3C組織對(duì)其的定義例如以下,它是一個(gè)軟件系統(tǒng),為了支持跨網(wǎng)絡(luò)的機(jī)器間相互操作交互而設(shè)計(jì)。Web Service服務(wù)通常被定義為一組模塊化的API,它們可以通過(guò)網(wǎng)絡(luò)進(jìn)行調(diào)用,來(lái)運(yùn)行遠(yuǎn)程系統(tǒng)的請(qǐng)求服務(wù)。
這里我們從一個(gè)程序猿的視角來(lái)觀察web service。在傳統(tǒng)的程序編碼中,存在這各種的函數(shù)方法調(diào)用。通常,我們知道一個(gè)程序模塊M中的方法A,向其發(fā)出調(diào)用請(qǐng)求,并傳入A方法須要的參數(shù)P,方法A運(yùn)行完畢后,返回處理結(jié)果R。這樣的函數(shù)或方法調(diào)用通常發(fā)生在同一臺(tái)機(jī)器上的同一程序語(yǔ)言環(huán)境下。如今的我們須要一種可以在不同計(jì)算機(jī)間的不同語(yǔ)言編寫(xiě)的應(yīng)用程序系統(tǒng)中,通過(guò)網(wǎng)絡(luò)通訊實(shí)現(xiàn)函數(shù)和方法調(diào)用的能力,而Web service正是應(yīng)這樣的需求而誕生的。
最普遍的一種說(shuō)法就是,Web Service = SOAP + HTTP + WSDL。當(dāng)中,SOAP Simple Object Access Protocol)協(xié)議是web service的主體,它通過(guò)HTTP或者SMTP等應(yīng)用層協(xié)議進(jìn)行通訊,自身使用XML文件來(lái)描寫(xiě)敘述程序的函數(shù)方法和參數(shù)信息,從而完畢不同主機(jī)的異構(gòu)系統(tǒng)間的計(jì)算服務(wù)處理。這里的WSDL(Web Services Description Language)web 服務(wù)描寫(xiě)敘述語(yǔ)言也是一個(gè)XML文檔,它通過(guò)HTTP向公眾公布,公告client程序關(guān)于某個(gè)詳細(xì)的 Web service服務(wù)的URL信息、方法的命名,參數(shù),返回值等。
以下,我們先來(lái)熟悉一下SOAP協(xié)議,看看它是怎樣描寫(xiě)敘述程序中的函數(shù)方法、參數(shù)及結(jié)果對(duì)象的。
SOAP協(xié)議簡(jiǎn)單介紹
什么是SOAP
SOAP 指簡(jiǎn)單對(duì)象訪問(wèn)協(xié)議,它是一種基于XML的消息通訊格式,用于網(wǎng)絡(luò)上,不同平臺(tái),不同語(yǔ)言的應(yīng)用程序間的通訊。可自己定義,易于擴(kuò)展。一條 SOAP 消息就是一個(gè)普通的 XML 文檔,包括下列元素:
? Envelope 元素,標(biāo)識(shí)XML 文檔一條 SOAP 消息
? Header 元素,包括頭部信息的XML標(biāo)簽
? Body 元素,包括全部的調(diào)用和響應(yīng)的主體信息的標(biāo)簽
? Fault 元素,錯(cuò)誤信息標(biāo)簽。
以上的元素都在 SOAP的命名空間http://www.w3.org/2001/12/soap-envelope中聲明;
SOAP的語(yǔ)法規(guī)則
? SOAP 消息必須用 XML 來(lái)編碼
? SOAP 消息必須使用 SOAP Envelope 命名空間
? SOAP 消息必須使用 SOAP Encoding 命名空間
? SOAP 消息不能包括 DTD 引用
? SOAP 消息不能包括 XML 處理指令
SOAP 消息的基本結(jié)構(gòu)
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 文檔是一個(gè)SOAP 消息。它的屬性 xmlns:soap的值必須是http://www.w3.org/2001/12/soap-envelope。
? encodingStyle 屬性,語(yǔ)法:soap:encodingStyle="URI"
encodingStyle 屬性用于定義文檔中使用的數(shù)據(jù)類型。此屬性可出如今不論什么 SOAP 元素中,并會(huì)被應(yīng)用到元素的內(nèi)容及元素的全部子元素上。
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 元素
通過(guò)沿著消息路徑經(jīng)過(guò)不同的端點(diǎn),SOAP 消息可從某個(gè)發(fā)送者傳播到某個(gè)接收者。并不是 SOAP 消息的全部部分均打算傳送到 SOAP 消息的終于端點(diǎn),只是,還有一個(gè)方面,或許打算傳送給消息路徑上的一個(gè)或多個(gè)端點(diǎn)。SOAP 的 actor 屬性可被用于將 Header 元素尋址到一個(gè)特定的端點(diǎn)。
SOAP 的 mustUnderstand 屬性可用于標(biāo)識(shí)標(biāo)題項(xiàng)對(duì)于要對(duì)其進(jìn)行處理的接收者來(lái)說(shuō)是強(qiáng)制的還是可選的。假如您向 Header 元素的某個(gè)子元素加入了 "mustUnderstand="1",則要求處理此頭部的接收者必須認(rèn)可此元素。
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>?? <? 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 元素可包括打算傳送到消息終于端點(diǎn)的實(shí)際 SOAP 消息。Body元素中既能夠包括SOAP定義的命名空間中的元素,如Fault,也能夠是用戶的應(yīng)用程序自己定義的元素。下面是一個(gè)用戶定義的請(qǐng)求:
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>?? <? 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>
上面的樣例請(qǐng)求蘋(píng)果的價(jià)格。請(qǐng)注意,上面的 m:GetPrice 和 Item 元素是應(yīng)用程序?qū)S玫脑亍K鼈儾⒎?SOAP 標(biāo)準(zhǔn)的一部分。而相應(yīng)的 SOAP 響應(yīng)應(yīng)該相似這樣:
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>?? <?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的錯(cuò)誤消息。它必須是 Body 元素的子元素,且在一條 SOAP 消息中,Fault 元素僅僅能出現(xiàn)一次。Fault 元素?fù)碛邢铝凶釉?#xff1a;
經(jīng)常使用的SOAP Fault Codes
HTTP協(xié)議中的SOAP 實(shí)例
以下的樣例中,一個(gè) GetStockPrice 請(qǐng)求被發(fā)送到了server。此請(qǐng)求有一個(gè) StockName 參數(shù),而在響應(yīng)中則會(huì)返回一個(gè) Price 參數(shù)。此功能的命名空間被定義在此地址中: "http://www.jsoso.net/stock"
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>?? 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>
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/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協(xié)議中的SOAP RPC工作流程
WSDL簡(jiǎn)單介紹
介紹過(guò)了SOAP,讓我們關(guān)注Web Service中另外一個(gè)重要的組成WSDL。
WSDL的主要文檔元素
WSDL文檔能夠分為兩部分。頂部分由抽象定義組成,而底部分則由詳細(xì)描寫(xiě)敘述組成。抽象部分以獨(dú)立于平臺(tái)和語(yǔ)言的方式定義SOAP消息,它們并不包括不論什么隨機(jī)器或語(yǔ)言而變的元素。這就定義了一系列服務(wù),截然不同的應(yīng)用都能夠?qū)崿F(xiàn)。詳細(xì)部分,如數(shù)據(jù)的序列化則歸入底部分,由于它包括詳細(xì)的定義。在上述的文檔元素中,<types>、<message>、<portType>屬于抽象定義層,<binding>、<service>屬于詳細(xì)定義層。全部的抽象能夠是單獨(dú)存在于別的文件里,也能夠從主文檔中導(dǎo)入。
WSDL文檔的結(jié)構(gòu)實(shí)例解析
以下我們將通過(guò)一個(gè)實(shí)際的WSDL文檔樣例來(lái)詳細(xì)說(shuō)明各標(biāo)簽的作用及關(guān)系。
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 version="1.0" encoding="UTF-8"?>
<definitionsxmlns: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:importnamespace="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:bindingtransport="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>
因?yàn)樯厦娴氖吕齒ML較長(zhǎng),我們將其逐段分解解說(shuō)
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>?? <definitionsxmlns: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>定義了文檔中用到的各個(gè)xml元素的namespace縮寫(xiě),也界定了本文檔自己的targetNamespace="http://www.jsoso.com/wstest",這意味著其他的XML要引用當(dāng)前XML中的元素時(shí),要聲明這個(gè)namespace。注意xmlns:tns="http://www.jsoso.com/wstest"這個(gè)聲明,它標(biāo)示了使用tns這個(gè)前綴指向自身的命名空間。
引用 WSDL文檔數(shù)據(jù)類型定義元素:<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><xsd:schema><xsd:importnamespace="http://www.jsoso.com/wstest"schemaLocation="http://localhost:8080/hello?xsd=1"></xsd:import></xsd:schema>
</types>
<types>標(biāo)簽定義了當(dāng)前的WSDL文檔用到的數(shù)據(jù)類型。要說(shuō)明的是,為了最大程度的平臺(tái)中立性,WSDL 使用 XML Schema 語(yǔ)法來(lái)定義數(shù)據(jù)類型。這些數(shù)據(jù)類型用來(lái)定義web service方法的參數(shù)和返回指。對(duì)于通用的原生數(shù)據(jù)類型如:integer , boolean , char , float等,在W3C的標(biāo)準(zhǔn)文檔http://www.w3.org/2001/XMLSchema中已經(jīng)做了定義。這里我們要引入的schema定義schemaLocation="http://localhost:8080/hello?xsd=1"是我們自己定義的Java對(duì)象類型。
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 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函數(shù)的參數(shù)。<message>元素中的每一個(gè)<part>子元素都和某個(gè)參數(shù)相符。輸入?yún)?shù)在<message>元素中定義,與輸出參數(shù)相隔離,輸出參數(shù)有自己的<message>元素。兼作輸入、輸出的參數(shù)在輸入輸出的<message>元素中有它們相應(yīng)的<part>元素。輸出<message>元素以"Response"結(jié)尾,對(duì)Java而言方法得返回值就相應(yīng)一個(gè)輸出的<message>。每一個(gè)<part>元素都有名字和類型屬性,就像函數(shù)的參數(shù)有參數(shù)名和參數(shù)類型。
在上面的文檔中有兩個(gè)輸入?yún)?shù)、兩個(gè)輸出參數(shù)和一個(gè)錯(cuò)誤參數(shù)(相應(yīng)Java中的Exception)。
? 輸入?yún)?shù)<message>的name屬性分別命名為toSayHello,sayHello。
toSayHello相應(yīng)輸入?yún)?shù)userName,參數(shù)類型為xsd:string,在Java語(yǔ)言中就是String;
sayHello相應(yīng)兩個(gè)輸入?yún)?shù)person和arg1,類型為tns:person和xsd:string。這里tns:person類型就是引用了< types >標(biāo)簽中的類型定義。
? 輸出參數(shù)<message>的name屬性分別命名為toSayHelloResponse和sayHelloResponse。
這個(gè)名稱和輸入?yún)?shù)的<message>標(biāo)簽name屬性相應(yīng),在其后面加上Response尾綴。
toSayHelloResponse相應(yīng)的返回值是returnWord,參數(shù)類型為xsd:string;
sayHelloResponse相應(yīng)的返回值是personList,參數(shù)類型為tns:personArray(自己定義類型);
? 錯(cuò)誤參數(shù)<message>的name屬性為HelloException。
它的<part>子標(biāo)簽element而不是type來(lái)定義類型。
以上的message標(biāo)簽的name屬性通常使用web service函數(shù)方法名作為參照,錯(cuò)誤參數(shù)標(biāo)簽則使用異常類名為參照。標(biāo)簽中的參數(shù)名稱,即part子元素的name屬性是可自己定義的(下一章節(jié)具體說(shuō)明)。message標(biāo)簽的參數(shù)類型將引用types標(biāo)簽的定義。
WSDL文檔函數(shù)體定義元素:< 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 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 元素。它可描寫(xiě)敘述一個(gè) web service、可被運(yùn)行的操作,以及相關(guān)的消息。portType的name屬性相應(yīng)Java中的一個(gè)服務(wù)類的類名。<portType> 元素使用其子元素< operation>描寫(xiě)敘述一個(gè)web service的服務(wù)方法。
在<operation>元素中,name屬性表示服務(wù)方法名,parameterOrder屬性表示方法的參數(shù)順序,使用空格符切割多個(gè)參數(shù),如:“parameterOrder="person arg1”。<operation>元素的子標(biāo)簽<input>表示輸入?yún)?shù)說(shuō)明,它引用<message>標(biāo)簽中的輸入?yún)?shù)。<output>表示輸出參數(shù)說(shuō)明,它引用<message>標(biāo)簽中的輸出參數(shù)。<fault>標(biāo)簽在Java方法中的特別用來(lái)表示異常(其他語(yǔ)言有相應(yīng)的錯(cuò)誤處理機(jī)制),它引用<message>標(biāo)簽中的錯(cuò)誤參數(shù)。
WSDL綁定實(shí)現(xiàn)定義元素:< 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 name="ExamplePortBinding" type="tns:Example"><soap:bindingtransport="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>標(biāo)簽是完整描寫(xiě)敘述協(xié)議、序列化和編碼的地方,<types>,<message>和<portType>標(biāo)簽處理抽象的數(shù)據(jù)內(nèi)容,而<binding>標(biāo)簽是處理傳輸數(shù)據(jù)的物理實(shí)現(xiàn)。
<binding>標(biāo)簽把前三部分的抽象定義詳細(xì)化。
首先<binding>標(biāo)簽使用<soap:binding>的transport和style屬性定義了Web Service的通訊協(xié)議HTTP和SOAP的請(qǐng)求風(fēng)格RPC。其次<operation>子標(biāo)簽將portType中定義的operation同SOAP的請(qǐng)求綁定,定義了操作名稱soapAction,輸出輸入?yún)?shù)和異常的編碼方式及命名空間。
WSDL服務(wù)地址綁定元素:< service >
Java代碼<service?name="Example"> ?? ??<port?name="ExamplePort"?binding="tns:ExamplePortBinding"> ?? ????<soap:address?location="http://localhost:8080/hello"></soap:address> ?? ??</port> ?? </service>?? <service name="Example"><port name="ExamplePort" binding="tns:ExamplePortBinding"><soap:address location="http://localhost:8080/hello"></soap:address></port>
</service>
service是一套<port>元素。在一一相應(yīng)形式下,每一個(gè)<port>元素都和一個(gè)location關(guān)聯(lián)。假設(shè)同一個(gè)<binding>有多個(gè)<port>元素與之關(guān)聯(lián),能夠使用額外的URL地址作為替換。
一個(gè)WSDL文檔中能夠有多個(gè)<service>元素,并且多個(gè)<service>元素十分實(shí)用,當(dāng)中之中的一個(gè)就是能夠依據(jù)目標(biāo)URL來(lái)組織端口。在一個(gè)WSDL文檔中,<service>的name屬性用來(lái)區(qū)分不同的service。在同一個(gè)service中,不同端口,使用端口的"name"屬性區(qū)分。
這一章節(jié),我們簡(jiǎn)單的描寫(xiě)敘述了WSDL對(duì)SOAP協(xié)議的支持,以及在Web Service中的作用。在接下來(lái)的章節(jié)中,我們將學(xué)習(xí)怎樣使用Java6.0的Annotation標(biāo)簽來(lái)定義和生成相應(yīng)的WSDL。
JavaSE6.0下的Web Service
從JavaSE6.0開(kāi)始,Java引入了對(duì)Web Service的原生支持。我們僅僅須要簡(jiǎn)單的使用Java的Annotation標(biāo)簽就可以將標(biāo)準(zhǔn)的Java方法公布成Web Service。(PS:Java Annotation資料請(qǐng)參考JDK5.0 Annotation學(xué)習(xí)筆記(一) )
但不是全部的Java類都能夠公布成Web Service。Java類若要成為一個(gè)實(shí)現(xiàn)了Web Service的bean,它須要遵循下邊這些原則:
以下我們將通過(guò)一個(gè)詳細(xì)的Java Web Service代碼樣例,配合上述的WSDL文件,講述怎樣編寫(xiě)JavaSE6.0的原生Web Service應(yīng)用。
完整的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服務(wù)的類 ?? ?*/ ?? @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>();; ?? ????/** ? ?????*? ? ?????*?返回一個(gè)字符串 ? ?????*?@param?userName ? ?????*?@return ? ?????*/?? ????@WebMethod(operationName="toSayHello",action="sayHello",exclude=false) ?? ????@WebResult(name="returnWord")//自己定義該方法返回值在WSDL中相關(guān)的描寫(xiě)敘述 ?? ????public?String?sayHello(@WebParam(name="userName")String?userName)?{ ?? ????????return?"Hello:"?+?userName; ?? ????} ?? ?? ????/** ? ?????*?web?services?方法的返回值與參數(shù)的類型不能為接口 ? ?????*?@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("說(shuō)hello出錯(cuò),對(duì)像為空。。"); ?? ????????} ?? ????????System.out.println(person.getName()?+?"?對(duì)?"?+?userName?+?"?說(shuō):Hello,我今年"?+?person.getAge()?+?"歲"); ?? ????????persons.add(person); ?? ????????return?persons.toArray(new?Person[0]); ?? ????} ?? }?? 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服務(wù)的類*/
@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>();;/*** * 返回一個(gè)字符串* @param userName* @return*/@WebMethod(operationName="toSayHello",action="sayHello",exclude=false)@WebResult(name="returnWord")//自己定義該方法返回值在WSDL中相關(guān)的描寫(xiě)敘述public String sayHello(@WebParam(name="userName")String userName) {return "Hello:" + userName;}/*** web services 方法的返回值與參數(shù)的類型不能為接口* @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("說(shuō)hello出錯(cuò),對(duì)像為空。。");}System.out.println(person.getName() + " 對(duì) " + userName + " 說(shuō):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標(biāo)簽主要將類暴露為WebService,當(dāng)中targetNamespace屬性定義了自己的命名空間,serviceName則定義了< definitions >標(biāo)簽和<service>標(biāo)簽的name屬性。
Annotation 2:@SOAPBinding(style=SOAPBinding.Style.RPC)
@SOAPBinding標(biāo)簽定義了WSDL文檔中SOAP的消息協(xié)議,當(dāng)中style屬性相應(yīng)SOAP的文檔類型,可選的有RPC和DOCUMENT
Annotation 3:@WebMethod(operationName="toSayHello",action="sayHello",exclude=false)
@WebMethod定義Web Service運(yùn)作的方法,
屬性action 相應(yīng)操作的活動(dòng) ,如<soap:operation soapAction="sayHello" />
屬性operationName匹配的wsdl:operation 的名稱,如<operation name="toSayHello" parameterOrder="userName">
屬性exclude 用于阻止將某一繼承方法公開(kāi)為web服務(wù),默覺(jué)得false
Annotation 4:@WebResult(name="returnWord")
@ WebResult定義方法返回值得名稱,如<part name="returnWord" type="xsd:string" />
Annotation 5:@WebParam(partName="person", mode=Mode.IN
@WebParam定義方法的參數(shù)名稱,如<part name="person" type="tns:person" />,當(dāng)中mode屬性表示參數(shù)的流向,可選值有IN / OUT / INOUT
這里要著重說(shuō)明的是,上述Web Service類的sayHello方法中,帶有HelloException這個(gè)異常聲明,造成該服務(wù)類不能直接公布成Web Service。須要使用wsgen工具為其生存異常Bean。關(guān)于wsgen工具的使用,請(qǐng)參考wsgen與wsimport命令說(shuō)明
公布一個(gè)的Java Web Service
在完畢了上述的Web Service Annotation凝視后,我們使用wsgen工具為其進(jìn)行服務(wù)資源文件的構(gòu)造(這里主要是生成一個(gè)名為org.jsoso.jws.server.jaxws.HelloExceptionBean的異常bean類),最后使用下面的類公布Web 服務(wù):
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?啟動(dòng)web?services服務(wù) ? ?*/?? public?class?StartServer?{ ?? ?? ????/** ? ?????*?@param?args ? ?????*/?? ????public?static?void?main(String[]?args)?{ ?? ????????/* ? ?????????*?生成Example?服務(wù)實(shí)例 ? ?????????*/?? ????????Example?serverBean?=?new?Example(); ?? ????????/* ? ?????????*?公布Web?Service到http://localhost:8080/hello地址 ? ?????????*/?? ????????Endpoint?endpoint?=? ?? ???????????Endpoint.publish("http://localhost:8080/hello",?serverBean); ?? ????????Binding?binding?=?endpoint.getBinding(); ?? ????????/* ? ?????????*?設(shè)置一個(gè)SOAP協(xié)議處理?xiàng)?? ?????????*?這里就簡(jiǎn)單得打印SOAP的消息文本 ? ?????????*/?? ????????List<Handler>?handlerChain?=?new?LinkedList<Handler>(); ?? ????????handlerChain.add(new?TraceHandler()); ?? ????????binding.setHandlerChain(handlerChain); ?? ????????System.out.println("服務(wù)已啟動(dòng)?http://localhost:8080/hello"); ?? ????} ?? }?? 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 啟動(dòng)web services服務(wù)*/
public class StartServer {/*** @param args*/public static void main(String[] args) {/** 生成Example 服務(wù)實(shí)例*/Example serverBean = new Example();/** 公布Web Service到http://localhost:8080/hello地址*/Endpoint endpoint = Endpoint.publish("http://localhost:8080/hello", serverBean);Binding binding = endpoint.getBinding();/** 設(shè)置一個(gè)SOAP協(xié)議處理?xiàng)? 這里就簡(jiǎn)單得打印SOAP的消息文本*/List<Handler> handlerChain = new LinkedList<Handler>();handlerChain.add(new TraceHandler());binding.setHandlerChain(handlerChain);System.out.println("服務(wù)已啟動(dòng) http://localhost:8080/hello");}
}
在控制臺(tái)執(zhí)行這個(gè)類,就能夠使用URL :http://localhost:8080/hello?wsdl 瀏覽到上文所描寫(xiě)敘述的WSDL的全文了。這說(shuō)明您的第一個(gè)Web Service應(yīng)用公布成功!
構(gòu)建Web Serviceclient
使用JavaSE6.0構(gòu)建Web Service的client是一件相當(dāng)簡(jiǎn)單的事。這里我們要使用到JDK中的還有一個(gè)命令行工具wsimport。在控制臺(tái)下輸入下面命令:
引用 wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref http://localhost:8080/hello?wsdl
就可以在包org.jsoso.jws.client.ref中生成client的存根及框架文件。當(dāng)中我們要使用的類僅僅有兩個(gè):服務(wù)類Example_Service和本地接口Example。編寫(xiě)例如以下client,就可以調(diào)用Web Service服務(wù):
Java代碼package?org.jsoso.jws.client; ?? ?? import?org.jsoso.jws.client.ref.*; ?? ?? public?class?RunClient?{ ?? ?? ????/** ? ?????*?@param?args ? ?????*/?? ????public?static?void?main(String[]?args)?{ ?? ????????//初始化服務(wù)框架類 ?? ????????Example_Service?service?=?new?Example_Service(); ?? ????????//或者本地服務(wù)借口的實(shí)例 ?? ????????Example?server?=?(Example)?service.getExamplePort(); ?? ????????try?{ ?? ????????????//調(diào)用web?service的toSayHello方法 ?? ????????????System.out.println("輸入toSayHello的返回值——"?+?server.toSayHello("阿土"));????????? ?? ?????????????Person?person?=?new?Person(); ?? ?????????????person.setName("阿土"); ?? ?????????????person.setAge(25); ?? ?????????????//調(diào)用web?service的sayHello方法 ?? ?????????????server.sayHello(person,?"機(jī)器人"); ?? ????????????? ?? ?????????????person?=?new?Person(); ?? ?????????????person.setName("aten"); ?? ?????????????person.setAge(30); ?? ?????????????//調(diào)用web?service的sayHello方法 ?? ?????????????PersonArray?list?=?server.sayHello(person,?"機(jī)器人"); ?? ????????????//輸出返回值 ?? ?????????????System.out.println("/n下面輸入sayHello的返回值——"); ?? ????????????for?(Person?p?:?list.getItem())?{ ?? ????????????????System.out.println(p.getName()?+?":"?+?p.getAge()); ?? ????????????}??????????? ?? ????????}?catch?(HelloException_Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????} ?? ????} ?? }?? package org.jsoso.jws.client;import org.jsoso.jws.client.ref.*;public class RunClient {/*** @param args*/public static void main(String[] args) {//初始化服務(wù)框架類Example_Service service = new Example_Service();//或者本地服務(wù)借口的實(shí)例Example server = (Example) service.getExamplePort();try {//調(diào)用web service的toSayHello方法System.out.println("輸入toSayHello的返回值——" + server.toSayHello("阿土")); Person person = new Person();person.setName("阿土");person.setAge(25);//調(diào)用web service的sayHello方法server.sayHello(person, "機(jī)器人");person = new Person();person.setName("aten");person.setAge(30);//調(diào)用web service的sayHello方法PersonArray list = server.sayHello(person, "機(jī)器人");//輸出返回值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的學(xué)習(xí)暫告一個(gè)段落。Java Web Service是一個(gè)相當(dāng)龐大的知識(shí)體系,當(dāng)中涉及的相關(guān)技術(shù)較多,這里無(wú)法一一道來(lái),我們將會(huì)在今后的開(kāi)發(fā)和使用中,同大家做進(jìn)一步深入的探討和學(xué)習(xí)。
附錄:wsgen與wsimport命令說(shuō)明
wsgen
wsgen是在JDK的bin文件夾下的一個(gè)exe文件(Windows版),該命令的主要功能是用來(lái)生成合適的JAX-WS。它讀取Web Service的終端類文件,同一時(shí)候生成全部用于公布Web Service所依賴的源碼文件和經(jīng)過(guò)編譯過(guò)的二進(jìn)制類文件。這里要特別說(shuō)明的是,通常在Web Service Bean中用到的異常類會(huì)另外生成一個(gè)描寫(xiě)敘述Bean,假設(shè)Web Service Bean中的方法有申明拋出異常,這一步是必需的,否則server無(wú)法綁定該對(duì)像。此外,wsgen還能輔助生成WSDL和相關(guān)的xsd文件。wsgen從資源文件生成一個(gè)完整的操作列表并驗(yàn)證web service是否合法,能夠完整公布。
命令參數(shù)說(shuō)明:
命令范例:wsgen -cp ./bin -r ./wsdl -s ./src -d ./bin -wsdl org.jsoso.jws.server.Example
wsimport
wsimport也是在JDK的bin文件夾下的一個(gè)exe文件(Windows版),主要功能是依據(jù)服務(wù)端公布的wsdl文件生成client存根及框架,負(fù)責(zé)與Web Service server通信,并在將其封裝成實(shí)例,client能夠直接使用,就像使用本地實(shí)例一樣。對(duì)Java而言,wsimport幫助程序猿生存調(diào)用web service所須要的client類文件.java和.class。要提醒指出的是,wsimport能夠用于非Java的server端,如:server端或許是C#編寫(xiě)的web service,通過(guò)wsimport則生成Java的client實(shí)現(xiàn)。
命令參數(shù)說(shuō)明:
命令范例:wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref http://localhost:8080/hello?wsdl??
Web Service的定義
W3C組織對(duì)其的定義例如以下,它是一個(gè)軟件系統(tǒng),為了支持跨網(wǎng)絡(luò)的機(jī)器間相互操作交互而設(shè)計(jì)。Web Service服務(wù)通常被定義為一組模塊化的API,它們可以通過(guò)網(wǎng)絡(luò)進(jìn)行調(diào)用,來(lái)運(yùn)行遠(yuǎn)程系統(tǒng)的請(qǐng)求服務(wù)。
這里我們從一個(gè)程序猿的視角來(lái)觀察web service。在傳統(tǒng)的程序編碼中,存在這各種的函數(shù)方法調(diào)用。通常,我們知道一個(gè)程序模塊M中的方法A,向其發(fā)出調(diào)用請(qǐng)求,并傳入A方法須要的參數(shù)P,方法A運(yùn)行完畢后,返回處理結(jié)果R。這樣的函數(shù)或方法調(diào)用通常發(fā)生在同一臺(tái)機(jī)器上的同一程序語(yǔ)言環(huán)境下。如今的我們須要一種可以在不同計(jì)算機(jī)間的不同語(yǔ)言編寫(xiě)的應(yīng)用程序系統(tǒng)中,通過(guò)網(wǎng)絡(luò)通訊實(shí)現(xiàn)函數(shù)和方法調(diào)用的能力,而Web service正是應(yīng)這樣的需求而誕生的。
最普遍的一種說(shuō)法就是,Web Service = SOAP + HTTP + WSDL。當(dāng)中,SOAP Simple Object Access Protocol)協(xié)議是web service的主體,它通過(guò)HTTP或者SMTP等應(yīng)用層協(xié)議進(jìn)行通訊,自身使用XML文件來(lái)描寫(xiě)敘述程序的函數(shù)方法和參數(shù)信息,從而完畢不同主機(jī)的異構(gòu)系統(tǒng)間的計(jì)算服務(wù)處理。這里的WSDL(Web Services Description Language)web 服務(wù)描寫(xiě)敘述語(yǔ)言也是一個(gè)XML文檔,它通過(guò)HTTP向公眾公布,公告client程序關(guān)于某個(gè)詳細(xì)的 Web service服務(wù)的URL信息、方法的命名,參數(shù),返回值等。
以下,我們先來(lái)熟悉一下SOAP協(xié)議,看看它是怎樣描寫(xiě)敘述程序中的函數(shù)方法、參數(shù)及結(jié)果對(duì)象的。
SOAP協(xié)議簡(jiǎn)單介紹
什么是SOAP
SOAP 指簡(jiǎn)單對(duì)象訪問(wèn)協(xié)議,它是一種基于XML的消息通訊格式,用于網(wǎng)絡(luò)上,不同平臺(tái),不同語(yǔ)言的應(yīng)用程序間的通訊。可自己定義,易于擴(kuò)展。一條 SOAP 消息就是一個(gè)普通的 XML 文檔,包括下列元素:
? Envelope 元素,標(biāo)識(shí)XML 文檔一條 SOAP 消息
? Header 元素,包括頭部信息的XML標(biāo)簽
? Body 元素,包括全部的調(diào)用和響應(yīng)的主體信息的標(biāo)簽
? Fault 元素,錯(cuò)誤信息標(biāo)簽。
以上的元素都在 SOAP的命名空間http://www.w3.org/2001/12/soap-envelope中聲明;
SOAP的語(yǔ)法規(guī)則
? SOAP 消息必須用 XML 來(lái)編碼
? SOAP 消息必須使用 SOAP Envelope 命名空間
? SOAP 消息必須使用 SOAP Encoding 命名空間
? SOAP 消息不能包括 DTD 引用
? SOAP 消息不能包括 XML 處理指令
SOAP 消息的基本結(jié)構(gòu)
Java代碼
SOAP Envelope 元素
Envelope 元素是 SOAP 消息的根元素。它指明 XML 文檔是一個(gè)SOAP 消息。它的屬性 xmlns:soap的值必須是http://www.w3.org/2001/12/soap-envelope。
? encodingStyle 屬性,語(yǔ)法:soap:encodingStyle="URI"
encodingStyle 屬性用于定義文檔中使用的數(shù)據(jù)類型。此屬性可出如今不論什么 SOAP 元素中,并會(huì)被應(yīng)用到元素的內(nèi)容及元素的全部子元素上。
Java代碼
SOAP Header 元素
- actor 屬性,語(yǔ)法soap:actor="URI"
通過(guò)沿著消息路徑經(jīng)過(guò)不同的端點(diǎn),SOAP 消息可從某個(gè)發(fā)送者傳播到某個(gè)接收者。并不是 SOAP 消息的全部部分均打算傳送到 SOAP 消息的終于端點(diǎn),只是,還有一個(gè)方面,或許打算傳送給消息路徑上的一個(gè)或多個(gè)端點(diǎn)。SOAP 的 actor 屬性可被用于將 Header 元素尋址到一個(gè)特定的端點(diǎn)。
- mustUnderstand 屬性 ,語(yǔ)法soap:mustUnderstand="0|1"
SOAP 的 mustUnderstand 屬性可用于標(biāo)識(shí)標(biāo)題項(xiàng)對(duì)于要對(duì)其進(jìn)行處理的接收者來(lái)說(shuō)是強(qiáng)制的還是可選的。假如您向 Header 元素的某個(gè)子元素加入了 "mustUnderstand="1",則要求處理此頭部的接收者必須認(rèn)可此元素。
Java代碼
SOAP Body 元素
必需的 SOAP Body 元素可包括打算傳送到消息終于端點(diǎn)的實(shí)際 SOAP 消息。Body元素中既能夠包括SOAP定義的命名空間中的元素,如Fault,也能夠是用戶的應(yīng)用程序自己定義的元素。下面是一個(gè)用戶定義的請(qǐng)求:
Java代碼
上面的樣例請(qǐng)求蘋(píng)果的價(jià)格。請(qǐng)注意,上面的 m:GetPrice 和 Item 元素是應(yīng)用程序?qū)S玫脑亍K鼈儾⒎?SOAP 標(biāo)準(zhǔn)的一部分。而相應(yīng)的 SOAP 響應(yīng)應(yīng)該相似這樣:
Java代碼
SOAP Fault 元素
Fault 元素表示 SOAP的錯(cuò)誤消息。它必須是 Body 元素的子元素,且在一條 SOAP 消息中,Fault 元素僅僅能出現(xiàn)一次。Fault 元素?fù)碛邢铝凶釉?#xff1a;
經(jīng)常使用的SOAP Fault Codes
HTTP協(xié)議中的SOAP 實(shí)例
以下的樣例中,一個(gè) GetStockPrice 請(qǐng)求被發(fā)送到了server。此請(qǐng)求有一個(gè) StockName 參數(shù),而在響應(yīng)中則會(huì)返回一個(gè) Price 參數(shù)。此功能的命名空間被定義在此地址中: "http://www.jsoso.net/stock"
- SOAP 請(qǐng)求:(注意HTTP的Head屬性)
Java代碼
- SOAP 響應(yīng):(注意HTTP的Head屬性)
Java代碼
HTTP協(xié)議中的SOAP RPC工作流程
WSDL簡(jiǎn)單介紹
介紹過(guò)了SOAP,讓我們關(guān)注Web Service中另外一個(gè)重要的組成WSDL。
WSDL的主要文檔元素
WSDL文檔能夠分為兩部分。頂部分由抽象定義組成,而底部分則由詳細(xì)描寫(xiě)敘述組成。抽象部分以獨(dú)立于平臺(tái)和語(yǔ)言的方式定義SOAP消息,它們并不包括不論什么隨機(jī)器或語(yǔ)言而變的元素。這就定義了一系列服務(wù),截然不同的應(yīng)用都能夠?qū)崿F(xiàn)。詳細(xì)部分,如數(shù)據(jù)的序列化則歸入底部分,由于它包括詳細(xì)的定義。在上述的文檔元素中,<types>、<message>、<portType>屬于抽象定義層,<binding>、<service>屬于詳細(xì)定義層。全部的抽象能夠是單獨(dú)存在于別的文件里,也能夠從主文檔中導(dǎo)入。
WSDL文檔的結(jié)構(gòu)實(shí)例解析
以下我們將通過(guò)一個(gè)實(shí)際的WSDL文檔樣例來(lái)詳細(xì)說(shuō)明各標(biāo)簽的作用及關(guān)系。
Java代碼
因?yàn)樯厦娴氖吕齒ML較長(zhǎng),我們將其逐段分解解說(shuō)
WSDL文檔的根元素:<definitions>
Java代碼
<definitions>定義了文檔中用到的各個(gè)xml元素的namespace縮寫(xiě),也界定了本文檔自己的targetNamespace="http://www.jsoso.com/wstest",這意味著其他的XML要引用當(dāng)前XML中的元素時(shí),要聲明這個(gè)namespace。注意xmlns:tns="http://www.jsoso.com/wstest"這個(gè)聲明,它標(biāo)示了使用tns這個(gè)前綴指向自身的命名空間。
引用 WSDL文檔數(shù)據(jù)類型定義元素:<types>
Java代碼
<types>標(biāo)簽定義了當(dāng)前的WSDL文檔用到的數(shù)據(jù)類型。要說(shuō)明的是,為了最大程度的平臺(tái)中立性,WSDL 使用 XML Schema 語(yǔ)法來(lái)定義數(shù)據(jù)類型。這些數(shù)據(jù)類型用來(lái)定義web service方法的參數(shù)和返回指。對(duì)于通用的原生數(shù)據(jù)類型如:integer , boolean , char , float等,在W3C的標(biāo)準(zhǔn)文檔http://www.w3.org/2001/XMLSchema中已經(jīng)做了定義。這里我們要引入的schema定義schemaLocation="http://localhost:8080/hello?xsd=1"是我們自己定義的Java對(duì)象類型。
WSDL文檔消息體定義元素:< message >
Java代碼
<message>元素定義了web service函數(shù)的參數(shù)。<message>元素中的每一個(gè)<part>子元素都和某個(gè)參數(shù)相符。輸入?yún)?shù)在<message>元素中定義,與輸出參數(shù)相隔離,輸出參數(shù)有自己的<message>元素。兼作輸入、輸出的參數(shù)在輸入輸出的<message>元素中有它們相應(yīng)的<part>元素。輸出<message>元素以"Response"結(jié)尾,對(duì)Java而言方法得返回值就相應(yīng)一個(gè)輸出的<message>。每一個(gè)<part>元素都有名字和類型屬性,就像函數(shù)的參數(shù)有參數(shù)名和參數(shù)類型。
在上面的文檔中有兩個(gè)輸入?yún)?shù)、兩個(gè)輸出參數(shù)和一個(gè)錯(cuò)誤參數(shù)(相應(yīng)Java中的Exception)。
? 輸入?yún)?shù)<message>的name屬性分別命名為toSayHello,sayHello。
toSayHello相應(yīng)輸入?yún)?shù)userName,參數(shù)類型為xsd:string,在Java語(yǔ)言中就是String;
sayHello相應(yīng)兩個(gè)輸入?yún)?shù)person和arg1,類型為tns:person和xsd:string。這里tns:person類型就是引用了< types >標(biāo)簽中的類型定義。
? 輸出參數(shù)<message>的name屬性分別命名為toSayHelloResponse和sayHelloResponse。
這個(gè)名稱和輸入?yún)?shù)的<message>標(biāo)簽name屬性相應(yīng),在其后面加上Response尾綴。
toSayHelloResponse相應(yīng)的返回值是returnWord,參數(shù)類型為xsd:string;
sayHelloResponse相應(yīng)的返回值是personList,參數(shù)類型為tns:personArray(自己定義類型);
? 錯(cuò)誤參數(shù)<message>的name屬性為HelloException。
它的<part>子標(biāo)簽element而不是type來(lái)定義類型。
以上的message標(biāo)簽的name屬性通常使用web service函數(shù)方法名作為參照,錯(cuò)誤參數(shù)標(biāo)簽則使用異常類名為參照。標(biāo)簽中的參數(shù)名稱,即part子元素的name屬性是可自己定義的(下一章節(jié)具體說(shuō)明)。message標(biāo)簽的參數(shù)類型將引用types標(biāo)簽的定義。
WSDL文檔函數(shù)體定義元素:< portType >
Java代碼
<portType> 元素是最重要的 WSDL 元素。它可描寫(xiě)敘述一個(gè) web service、可被運(yùn)行的操作,以及相關(guān)的消息。portType的name屬性相應(yīng)Java中的一個(gè)服務(wù)類的類名。<portType> 元素使用其子元素< operation>描寫(xiě)敘述一個(gè)web service的服務(wù)方法。
在<operation>元素中,name屬性表示服務(wù)方法名,parameterOrder屬性表示方法的參數(shù)順序,使用空格符切割多個(gè)參數(shù),如:“parameterOrder="person arg1”。<operation>元素的子標(biāo)簽<input>表示輸入?yún)?shù)說(shuō)明,它引用<message>標(biāo)簽中的輸入?yún)?shù)。<output>表示輸出參數(shù)說(shuō)明,它引用<message>標(biāo)簽中的輸出參數(shù)。<fault>標(biāo)簽在Java方法中的特別用來(lái)表示異常(其他語(yǔ)言有相應(yīng)的錯(cuò)誤處理機(jī)制),它引用<message>標(biāo)簽中的錯(cuò)誤參數(shù)。
WSDL綁定實(shí)現(xiàn)定義元素:< binding >
Java代碼
<binding>標(biāo)簽是完整描寫(xiě)敘述協(xié)議、序列化和編碼的地方,<types>,<message>和<portType>標(biāo)簽處理抽象的數(shù)據(jù)內(nèi)容,而<binding>標(biāo)簽是處理傳輸數(shù)據(jù)的物理實(shí)現(xiàn)。
<binding>標(biāo)簽把前三部分的抽象定義詳細(xì)化。
首先<binding>標(biāo)簽使用<soap:binding>的transport和style屬性定義了Web Service的通訊協(xié)議HTTP和SOAP的請(qǐng)求風(fēng)格RPC。其次<operation>子標(biāo)簽將portType中定義的operation同SOAP的請(qǐng)求綁定,定義了操作名稱soapAction,輸出輸入?yún)?shù)和異常的編碼方式及命名空間。
WSDL服務(wù)地址綁定元素:< service >
Java代碼
service是一套<port>元素。在一一相應(yīng)形式下,每一個(gè)<port>元素都和一個(gè)location關(guān)聯(lián)。假設(shè)同一個(gè)<binding>有多個(gè)<port>元素與之關(guān)聯(lián),能夠使用額外的URL地址作為替換。
一個(gè)WSDL文檔中能夠有多個(gè)<service>元素,并且多個(gè)<service>元素十分實(shí)用,當(dāng)中之中的一個(gè)就是能夠依據(jù)目標(biāo)URL來(lái)組織端口。在一個(gè)WSDL文檔中,<service>的name屬性用來(lái)區(qū)分不同的service。在同一個(gè)service中,不同端口,使用端口的"name"屬性區(qū)分。
這一章節(jié),我們簡(jiǎn)單的描寫(xiě)敘述了WSDL對(duì)SOAP協(xié)議的支持,以及在Web Service中的作用。在接下來(lái)的章節(jié)中,我們將學(xué)習(xí)怎樣使用Java6.0的Annotation標(biāo)簽來(lái)定義和生成相應(yīng)的WSDL。
JavaSE6.0下的Web Service
從JavaSE6.0開(kāi)始,Java引入了對(duì)Web Service的原生支持。我們僅僅須要簡(jiǎn)單的使用Java的Annotation標(biāo)簽就可以將標(biāo)準(zhǔn)的Java方法公布成Web Service。(PS:Java Annotation資料請(qǐng)參考JDK5.0 Annotation學(xué)習(xí)筆記(一) )
但不是全部的Java類都能夠公布成Web Service。Java類若要成為一個(gè)實(shí)現(xiàn)了Web Service的bean,它須要遵循下邊這些原則:
- ? 這個(gè)類必須是public類
- ? 這些類不能是final的或者abstract
- ? 這個(gè)類必須有一個(gè)公共的默認(rèn)構(gòu)造函數(shù)
- ? 這個(gè)類絕對(duì)不能有finalize()方法
以下我們將通過(guò)一個(gè)詳細(xì)的Java Web Service代碼樣例,配合上述的WSDL文件,講述怎樣編寫(xiě)JavaSE6.0的原生Web Service應(yīng)用。
完整的Java Web Service類代碼
Java代碼
Annotation 1@WebService(name="Example", targetNamespace="http://www.jsoso.com/wstest", serviceName="Example")
@WebService標(biāo)簽主要將類暴露為WebService,當(dāng)中targetNamespace屬性定義了自己的命名空間,serviceName則定義了< definitions >標(biāo)簽和<service>標(biāo)簽的name屬性。
Annotation 2:@SOAPBinding(style=SOAPBinding.Style.RPC)
@SOAPBinding標(biāo)簽定義了WSDL文檔中SOAP的消息協(xié)議,當(dāng)中style屬性相應(yīng)SOAP的文檔類型,可選的有RPC和DOCUMENT
Annotation 3:@WebMethod(operationName="toSayHello",action="sayHello",exclude=false)
@WebMethod定義Web Service運(yùn)作的方法,
屬性action 相應(yīng)操作的活動(dòng) ,如<soap:operation soapAction="sayHello" />
屬性operationName匹配的wsdl:operation 的名稱,如<operation name="toSayHello" parameterOrder="userName">
屬性exclude 用于阻止將某一繼承方法公開(kāi)為web服務(wù),默覺(jué)得false
Annotation 4:@WebResult(name="returnWord")
@ WebResult定義方法返回值得名稱,如<part name="returnWord" type="xsd:string" />
Annotation 5:@WebParam(partName="person", mode=Mode.IN
@WebParam定義方法的參數(shù)名稱,如<part name="person" type="tns:person" />,當(dāng)中mode屬性表示參數(shù)的流向,可選值有IN / OUT / INOUT
這里要著重說(shuō)明的是,上述Web Service類的sayHello方法中,帶有HelloException這個(gè)異常聲明,造成該服務(wù)類不能直接公布成Web Service。須要使用wsgen工具為其生存異常Bean。關(guān)于wsgen工具的使用,請(qǐng)參考wsgen與wsimport命令說(shuō)明
公布一個(gè)的Java Web Service
在完畢了上述的Web Service Annotation凝視后,我們使用wsgen工具為其進(jìn)行服務(wù)資源文件的構(gòu)造(這里主要是生成一個(gè)名為org.jsoso.jws.server.jaxws.HelloExceptionBean的異常bean類),最后使用下面的類公布Web 服務(wù):
Java代碼
在控制臺(tái)執(zhí)行這個(gè)類,就能夠使用URL :http://localhost:8080/hello?wsdl 瀏覽到上文所描寫(xiě)敘述的WSDL的全文了。這說(shuō)明您的第一個(gè)Web Service應(yīng)用公布成功!
構(gòu)建Web Serviceclient
使用JavaSE6.0構(gòu)建Web Service的client是一件相當(dāng)簡(jiǎn)單的事。這里我們要使用到JDK中的還有一個(gè)命令行工具wsimport。在控制臺(tái)下輸入下面命令:
引用 wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref http://localhost:8080/hello?wsdl
就可以在包org.jsoso.jws.client.ref中生成client的存根及框架文件。當(dāng)中我們要使用的類僅僅有兩個(gè):服務(wù)類Example_Service和本地接口Example。編寫(xiě)例如以下client,就可以調(diào)用Web Service服務(wù):
Java代碼
屆此,本次Web Service的學(xué)習(xí)暫告一個(gè)段落。Java Web Service是一個(gè)相當(dāng)龐大的知識(shí)體系,當(dāng)中涉及的相關(guān)技術(shù)較多,這里無(wú)法一一道來(lái),我們將會(huì)在今后的開(kāi)發(fā)和使用中,同大家做進(jìn)一步深入的探討和學(xué)習(xí)。
附錄:wsgen與wsimport命令說(shuō)明
wsgen
wsgen是在JDK的bin文件夾下的一個(gè)exe文件(Windows版),該命令的主要功能是用來(lái)生成合適的JAX-WS。它讀取Web Service的終端類文件,同一時(shí)候生成全部用于公布Web Service所依賴的源碼文件和經(jīng)過(guò)編譯過(guò)的二進(jìn)制類文件。這里要特別說(shuō)明的是,通常在Web Service Bean中用到的異常類會(huì)另外生成一個(gè)描寫(xiě)敘述Bean,假設(shè)Web Service Bean中的方法有申明拋出異常,這一步是必需的,否則server無(wú)法綁定該對(duì)像。此外,wsgen還能輔助生成WSDL和相關(guān)的xsd文件。wsgen從資源文件生成一個(gè)完整的操作列表并驗(yàn)證web service是否合法,能夠完整公布。
命令參數(shù)說(shuō)明:
- ? -cp 定義classpath
- ? -r 生成 bean的wsdl文件的存放文件夾
- ? -s 生成公布Web Service的源碼文件的存放文件夾(假設(shè)方法有拋出異常,則會(huì)生成該異常的描寫(xiě)敘述類源文件)
- ? -d 生成公布Web Service的編譯過(guò)的二進(jìn)制類文件的存放文件夾(該異常的描寫(xiě)敘述類的class文件)
命令范例:wsgen -cp ./bin -r ./wsdl -s ./src -d ./bin -wsdl org.jsoso.jws.server.Example
wsimport
wsimport也是在JDK的bin文件夾下的一個(gè)exe文件(Windows版),主要功能是依據(jù)服務(wù)端公布的wsdl文件生成client存根及框架,負(fù)責(zé)與Web Service server通信,并在將其封裝成實(shí)例,client能夠直接使用,就像使用本地實(shí)例一樣。對(duì)Java而言,wsimport幫助程序猿生存調(diào)用web service所須要的client類文件.java和.class。要提醒指出的是,wsimport能夠用于非Java的server端,如:server端或許是C#編寫(xiě)的web service,通過(guò)wsimport則生成Java的client實(shí)現(xiàn)。
命令參數(shù)說(shuō)明:
- ? -d 生成client運(yùn)行類的class文件的存放文件夾
- ? -s 生成client運(yùn)行類的源文件的存放文件夾
- ? -p 定義生成類的包名
命令范例:wsimport -d ./bin -s ./src -p org.jsoso.jws.client.ref http://localhost:8080/hello?wsdl??
轉(zhuǎn)載于:https://www.cnblogs.com/blfshiye/p/4384446.html
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Web Service学习笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 团队开发--手机小游戏 需求分析
- 下一篇: bzoj 3282