定義了web service的server端和client端請求和回應的格式,主要有5個,分別是types、message、portType、binding、service
一個web service對應唯一的wsdl文件
.SOAP:simple object access protocal
簡單的、基于HTTP和XML的、用在WEB上交換資料的
soap訊息:請求/回應訊息
http+xml片斷
.SEI:WebService EndPoint Interface
WebService server端用來處理請求的介面,java就是寫一個介面,上面加上@WebService
.CXF:Celtix + XFire
XFire原本就是webservice框架,可能有一些問題,修改bug後,加上Celtix變成新的框架,是apache開發的,用來開發webservice的server端和client端
※SEI和實作
package hello.test; import javax.jws.WebService; @WebService public interface IHelloWS { public void hello(); } -------------------- package hello.test; import javax.jws.WebService; @WebService(endpointInterface = "hello.test.IHelloWS") public class HelloWS implements IHelloWS { @Override public void hello() { System.out.println("Hello WebService!"); } public String add(Integer a, int b) { System.out.println("call add method!"); return "Answer is " + (a + b); } }
※
※啟動伺服器
package hello.test; import javax.xml.ws.Endpoint; public class MyServer { public static void main(String[] args) { String url = "http://localhost:8888/aaa/bbb/ccc"; Endpoint.publish(url, new HelloWS()); System.out.println("server is start!"); } }
※如有修改(SEI、SEI實作或這一隻測試類),必須重啟才會生效
※複製url到瀏覽器,最後加上「?wsdl」會有如下的xml格式,也就是wsdl
<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://test.hello/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://test.hello/" name="HelloWSService"> <types> <xsd:schema> <xsd:import namespace="http://test.hello/" schemaLocation="http://localhost:8888/aaa/bbb/ccc?xsd=1"/> </xsd:schema> </types> <message name="hello"> <part name="parameters" element="tns:hello"/> </message> <message name="helloResponse"> <part name="parameters" element="tns:helloResponse"/> </message> <portType name="IHelloWS"> <operation name="hello"> <input wsam:Action="http://test.hello/IHelloWS/helloRequest" message="tns:hello"/> <output wsam:Action="http://test.hello/IHelloWS/helloResponse" message="tns:helloResponse"/> </operation> </portType> <binding name="HelloWSPortBinding" type="tns:IHelloWS"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="hello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> </input> <output> <soap:body use="literal"/> </output> </operation> </binding> <service name="HelloWSService"> <port name="HelloWSPort" binding="tns:HelloWSPortBinding"> <soap:address location="http://localhost:8888/aaa/bbb/ccc"/> </port> </service> </definitions>
※types最裡層還有一個import的網址,再開一個瀏覽器分頁,會發現它長如下的樣子
<xs:schema xmlns:tns="http://test.hello/" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://test.hello/"> <xs:element name="hello" type="tns:hello"/> <xs:element name="helloResponse" type="tns:helloResponse"/> <xs:complexType name="hello"> <xs:sequence/> </xs:complexType> <xs:complexType name="helloResponse"> <xs:sequence/> </xs:complexType> </xs:schema>
※格式內容下一篇再說,先說明SEI的部分
※@WebService有6個屬性,都是接收String,分別是
name、targetNamespace、serviceName、portName、wsdlLocation、endpointInterface
其中EI(也就是interface)只有3個合法值:name、targetNamespace、wsdlLocation,API有特別註明「This member-value is not allowed on endpoint interfaces.」,
其中name和targetNamespace屬性,如果EI和實作類的設定不同,會以EI為主
wsdlLocation只能設在SEI裡,如果實作類有設,會在執行時出「[failed to localize] cannot.load.wsdl(path/xxx.wsdl)」的錯
.endpointInterface
上面有用到,沒加也是可以run,但客戶端請求時有所不同,下面會說明
有加只會將有實作的部分暴露出來
沒加會將自己的方法暴露出來
一個方法會對應
兩個message
兩個schema(在import裡)
一個portType-->operation
一個binding-->operation
.targetNamespace:,指定targetNamespace,不加為「http://從右到左的包名/」,此例為「http://test.hello/」
.serviceName:指定serviceName,不加為「實作類別名+Service」,此例為「HelloWSService」
.name:指定portType的name屬性,不加為「介面名」,此例為「IHelloWS」
還會影響portType-->operation-->input/output的wsam:Action屬性,這個屬性是「targetNamespace名稱/name名稱/方法名稱+Request/Response」,input為Request;output為Response
,此例input為「http://test.hello/HelloWS/addRequest」;output為「http://test.hello/HelloWS/addResponse」
.portName:指定service-->port的name屬性,不加為「實作類別名+Port」,此例為「HelloWSPort」;
還會影響binding的name屬性,為「port的name屬性+Binding」,此例為「HelloWSPortBinding」
.wsdlLocation:指定wsdl的路徑
※還有@WebResult、@WebParam、@WebMethod,這三個annotation也很重要
@WebResult寫在方法上,name屬性可以設定xml的回傳值的名稱,不打為「return」
@WebParam寫在參數裡,name屬性可以設定xml的參數名稱,不打為「arg+數字(從0開始)」
@WebMethod 寫在方法上,有三個值operationName、action、exclude
operationName屬性可以設定message的name屬性和portType-->operation的name屬性,不打為方法名
exclude不能加在EI,預設為false,若為true,此方法不會暴露出來
※在definitions有很多屬性,其中targetNamespace和name下面程式的QName會用到,分別對應到第一和第二個參數
※Eclipse 的 Web Services Explorer
其實Eclipse內鍵也有一個瀏覽的工具,也類似於在網址上打XXX~?wsdl,裡面可以對wsdl做request、response,如下畫面1.首先先切換到Java EE視圖
2.按Launch the Web Services Explorer,或者Run-->Launch the Web Services Explorer也是一樣
3.右上角有個WSDL Page
4.將剛剛網址的wsdl貼上後按Go,每按一次Go,左邊的Navigator都會出現一個你打的網址,就算你打的網址一樣也會出現
想刪除,只要在左框(Navigator)的右上角Clear即可
※JavaClientCallWebService.java
package xxx.ooo; import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; public class JavaClientCallWebService { public static void main(String[] args) { Url url = null; try { url = new URL("http://localhost:8888/aaa/bbb/ccc?wsdl"); QName qname = new QName("http://test.hello/", "HelloWSService"); Service service = Service.create(url, qname); IHelloWS call = service.getPort(IHelloWS.class); call.hello(); } catch (MalformedURLException e) { e.printStackTrace(); } } }
※上面是用Eclipse內鍵的工具呼叫WebService,這裡是用寫程式的方式呼叫
※將wsdl裡面的definitions的targetNamespace和name放在QName的建構子裡即可
※如果實作類的@WebService沒有endpointInterface屬性,會報「javax.xml.ws.WebServiceException: 未定義的連接埠類型: {http://test.hello/}IHelloWS」或者「javax.xml.ws.WebServiceException: {http://test.hello/}HelloWSService 不是有效的連接埠. 有效的連接埠為: {http://test.hello/}HelloWSPort」,應該是一定要有interface了
※Eclipse會多一個Console,一個是伺服器端的;一個是用戶端的
但hello()是伺服器端的程式碼,所以「Hello WebService!」會印在伺服器端
※wsimport
在安裝java的路徑裡,例如C:\Program Files\Java\jdkXXX\bin,有一個wsimport工具(jre沒有)它可以將我們剛剛寫的程式顯示出完整的程式碼,因為有很多預設值,所以上面的程式碼才會這麼少
此時在命令提示字元下使用這個命令:
wsimport -d d:\ws_practice -keep -verbose wsdl網址
-d後面接路徑,表示會將生成出來的.class檔放在這個路徑裡,不指定會在目前的目錄生成
-keep表示還會保留.java檔
-verbose表示生成的過程有畫面可以看
此時切換好路徑後,輸入 wsimport -keep http://localhost:8888/aaa/bbb/ccc?wsdl
新增一個project,package和之前一樣,然後將生成的程式碼複製進去,.java部分即可
後面的網址也可以是wsdl格式的檔案,例如將這段網址的結果複製到一個文字文件,然後下
wsimport -keep -d d:\xxx d:\wsdl_file.wsdl,也是可以,副檔不一定要wsdl,只要是純文字即可
※test.java
HelloWSService hws = new HelloWSService(); IHelloWS hw = hws.getHelloWSPort(); System.out.println(hw.getClass()); hw.hello();
※這支是使用wsimport幫我們生成的程式碼,測試用戶端的程式
※首先先用wsdl最下面的service的name屬性名稱,然後一定有個方法可以返回介面
service下有個port標籤,他的name屬性就是getXXX
所以以這個例子就是getHelloWSPort
※可以發現 hw.getClass()是用代理的
※TCP/IP Monitor
Eclipse有內鍵一個TCP/IP監控工具首先將網址的wsdl存成一個檔案,然後將最下面的代碼改成自己監聽的port
<service name="HelloWSService"> <port name="HelloWSPort" binding="tns:HelloWSPortBinding"> <soap:address location="http://localhost:9999/aaa/bbb/ccc"/> </port> </service>
※我是將8888改成9999
最後在Window-->Preferences,然後如下畫面設定:
設定後還要啟動才可以
如果要修改,必需先按左邊的設定,然後右邊的Stop,再按Edit
※程式執行和wsimport的test.java一樣,執行後會出現一個叫TCP/IP Monitor的View
如果沒出現,就要選Window-->Show View-->Other...後,會出現如下的畫面:
TCP/IP Monitor畫面如下:
左上的紅框是只要有一次請求就會出現一次,不管錯誤還是一樣的請求都會出現
左邊為Request;右邊為Response
選了XML就會出現最下面的SOAP格式的標籤,這個標籤在上面介紹的Web Services Exporer也有,如果選Web Browser會比較好看
※SOAP UI 工具
此工具的功能和Eclipse 內鍵的 Web Services Explorer一樣,可以分析wsdl的結構,但工作中還蠻流行的,在此介紹一下,這裡是官網,可到此下載P.S 上面介紹的wsimport將wsdl轉成java代碼,可以在網路上搜尋「免費 webservice」,可以找到xxx.wsdl的網址,但不一定能成功轉成java代碼,因為有可能是其他語言產生出來的
但用Eclipse 內鍵的 Web Services Explorer 和 SOAP UI 都可以執行
※為了要測試有參數的情形,所以介面增加一個方法並實作
public String add(Integer a, int b); -------------------- @Override public String add(Integer a, int b) { System.out.println("call add method!"); return "Answer is " + (a + b); }
※安裝好後如下設定:
1.按紅框的SOAP,
2.Project按右鍵--> New SOAP Project
3.File--> New SOAP Project
4.Ctrl+N
在紅框打上xxx.wsdl後,Project Name會自動代出值來,預設是帶最後的「/」之後的字串,可以改
最左邊可看到兩個方法,add是我剛才加的,按下去會出現右邊的圖
argX是打參數的地方,打完後按下綠色的三角形就會執行
最右邊就會出現結果,如果有在控制台打印字串,也會出現在 server 端的 console