前言:
随着近幾年來,SOA,EAI等架構體系的日漸成熟,Webservice越來越熾手可熱,尤其是在企業做異質平台整合時成為了首選的技術。
Java的Webservice技術更是層出不窮,比較流行的有:
Axis2,Spring WS以及Jaxws。
本人在日常工作和以往project中,在使用了上述這些Webservice後進行了總結,比較,終于認為jaxws是眼下最标準,須要額外第三方插件最少,配置最少最靈活的webservice。
JAXWS适合差點兒全部Webserviceclient的調用,是以不少巨頭型的廠商如:IBM,Weblogic等,在他們的産品上都使用了以JAXWS為标準的Webservice接口。
本教程分成五天,為0基礎教程。
通過本教程,能夠使一個沒有Webservice概念或者沒有寫過Webservice的JAVA Resource高速上手入門,并能滿足一般中小型項目中Webservice的應用。
對于Webservice Security,在(0基礎)教程中并不提供,會在進階教程中具體描寫叙述。
隻是真正利用Webservice Security特性即XML加密技術的project并不多,少之又少,大多還是以http: //xxx/xxxService?userId=&password=這種形式來進行“假安全”通訊的。
必經我們的大部分項目是執行在Intranet裡的,并且有非常好的監控和布防。
以下開始我們的教程。
第一天
目标:
1. 了解jaxws
2. 寫jaxws之前的準備工作
3. 一切始于HelloWorld
4. 了解同步,異步
一、了解jaxws
1.1JAX-WS概述
JAX-WS2.0 的全稱為 Java API for XML-Based Webservices (JAX-WS) 2.0。JAX-WS 2.0 是對 JAX-RPC 1.0 規範的擴充,是 JAX-RPC 1.1 的興許版本号, JAX-RPC 2.0 标準公布不久後便被又一次命名為 JAX-WS 2.0。 JAX-WS 2.0 是面向 Java 5 的開發 Web services 的最新程式設計标準,它提供了新的程式設計模型和對以往的 JAX-RPC 方式的 Web services 進行了增強。 JAX-WS2.0 (JSR 224)是Sun新的web services協定棧,是一個全然基于标準的實作。在binding層,使用的是the Java Architecture for XMLBinding (JAXB, JSR 222),在parsing層,使用的是the Streaming API for XML (StAX, JSR 173),同一時候它還全然支援schema規範。
1.2JAX-WS 2.1特性
支援SOAP 1.1(預設)、1.2
支援XML/HTTP Binding
支援WS-Addressing
支援document/literal樣式
支援WS-I Basic Profile 1.1
支援消息傳輸優化機制(Message Transmission Optimization Mechanism,MTOM)
二、寫jaxws之前的準備工作
2.1JDKjavaversion "1.6.0_x"。
2.2JAX-WS RI 2.1.1 in JDK 6
2.2.1 JAX-WS RI 2.1.1安裝注解
JAX-WS RI元件下載下傳後為一個”.jar”檔案,它并不能直接在project中使用,它是一個以JAVA Swing為界面的JAXWS的安裝程式包。
我們須要打開一個指令行窗體,并輸入:
輸入完這條指令後,你會得到一個安裝界面例如以下:
下一步,下一步完畢安裝後你會得到這種一個檔案夾:
這個檔案夾裡有我們寫JAXWS所需的全部lib包以及JAXWS自帶的教程。
三、一切始于HelloWorld
3.1建立Webservice的Server端project
能夠看到我們這個檔案夾除傳統的src,WebContent檔案夾外還有幾個檔案夾,它們各自是:
? build
? wsdl
? wssrc
我們來書寫我們的第一個Webservice吧,它的名字叫Hello(Come on, 老套了,又來了)。
package ctsjavacoe.ws.fromjava; import javax.jws.WebMethod; import javax.jws.WebService; @WebService public class Hello { @WebMethod public String say(String name) { return ("Hello: "+name); } } |
注意:
@WebService
凝視在了Class之上,這告訴了JAXWS,此類為Webservice。
@WebMethod
凝視在了public方法上,這告訴了JAXWS,此方法為soap方法,該方法有兩個參數,一個input的String,一個output的String。
業務邏輯非常easy,client調用傳入一個Name,服務端傳回給client一個”Hello: “+name的字串。
如今我們通過Java檔案來生成Webservice相關布署檔案以及調用接口。
3.2通過Java類編譯Webservice
JAX-WS 2.0 有兩種開發過程:自頂向下和自底向上。自頂向下方式指通過一個 WSDL 檔案來建立Web Service,自底向上是從 Java 類出發建立 Web Service。兩種開發過程終于形成的檔案包含:
1.SEI。一個SEI相應WSDL中WebService的一個port,在Java中是一個Java接口。
2.SEI實作類。
3.WSDL和XSD檔案。
結合公司内項目的特點,我們很多其它的是碰到以下兩種情況:
1. Onsite要我們做一個Webservice或者是客戶要求我們提供Webservice接口;
2. Onsite已經有一個Webservice了,如今要我們做client內建。
是以,我們選用Server端通過Java Class生成webservice,而client通過wsdl生成Java調用類的做法。
JAXWS為我們提供了兩個工具:
ü wsgen
主要用于Server端通過Java類編譯成Webservice及相關的wsdl檔案
ü wsimport
主要用于Client端(調用端)通過wsdl編譯出調用Server端的Java檔案
我們就來生成一下上面的這個Hello,打開一個command窗體,鍵入例如以下指令:
-wsdl參數代表生成webservice
- s參數代表生成的.java檔案置于何處
-d 參數代表生成的編譯class檔案置于何處(這個能夠忽略,我們利用eclipse編譯)
-r 參數代表生成的.wsdl檔案與.xsd檔案生成在何處
-cp參數代表classpath,即Hello.class的所在,為什麼我們的-cp後是這麼長一個路徑呢?請看Eclipse裡project編譯輸出檔案夾的路徑就知道了:
3.2.1 生成的src檔案
好,我們如今回到eclipseproject裡,重新整理一下project:
看到在wssrc檔案夾下已經生成了我們所需的java檔案了,請手工cut(對,是cut)這些檔案到我們project的”src”檔案夾,假設不cut,下次繼續使用該檔案夾生成webservice類時,wsgen有時會生成不了,但也不報錯,不知道為什麼,查了一下,可能是一個bug,因該會在興許的jdk1.6.30up裡改進。
3.2.2 生成的wsdl及xsd檔案
這裡我們有兩個檔案,一個是wsdl檔案,這個就是我們的webservice的entry,一個是xsd檔案,這個是什麼?
這個就是我們java的方法裡的參數的相應,或者換句話說,它就是xml格式的java bean,在webservice的世界裡,xsd是作為資料結構描寫叙述用的。
如今我們的webservice的服務端有了。
3.2.3 布署webservice
布署前的準備:
在project的WEB-INF檔案夾下建立“sun-jaxws.xml”檔案,内容例如以下:
<?xml version="1.0" encoding="UTF-8"?> <endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'> <endpoint name='Hello' implementation='ctsjavacoe.ws.fromjava.Hello' url-pattern='/HelloService' /> </endpoints> |
将ctsjavacoe.ws.fromjava.Hello聲明為Web Service。
假設是從WSDL生成的Web Service,則寫法為,
<?xml version="1.0" encoding="UTF-8"?> <endpoints version="2.0" xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"> <endpoint implementation="ctsjavacoe.ws.fromjava.HelloSEI" name="Hello" url-pattern="/HelloService" /> </endpoints> |
改動WEB-INF檔案夾下的web.xml檔案,增加例如以下内容:
<servlet> <servlet-name>Hello</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Hello</servlet-name> <url-pattern>/HelloService</url-pattern> </servlet-mapping> |
為每個WebService聲明一個com.sun.xml.ws.transport.http.servlet.WSServlet。
開始布署:
1. 在tomcat的webapps檔案夾下建立一個檔案夾叫“D: omcat2webappsJaxWSSample”
2. 把eclipseprojectJaxWSSample下WebContent檔案夾下全部的東西copy至該檔案夾下
3. 重新啟動tomcat
在ie中輸入:
http://localhost:9090/JaxWSSample/HelloService?wsdl
能夠看到我們的webservice已經生成了。
3.3通過Server端的WSDL生成供JAVA調用的client
3.3.1 同步與異步
同步調用,非常好了解,即一來一回,Client端request到Server端,Sever端立馬回一個response。
異步調用,就是client調用一次服務端後,服務端處理事務并非即時傳回的,比方說傳一個600MB檔案給服務端,服務端在處理接收和解析檔案時,client不會立即得到一個響應,它會等待一段時間,等server處理完後,再通知client“我處理完了”。
3.3.2 利用wsimport産生client
我們建立一個eclipse的project,僅僅須要是JAVAproject即可了,不須要webproject的,由于我們這邊僅僅用代碼做調用:
把Server端的wsdl及xsd都手工copy到clientproject的wsdl檔案夾下。
打開一個cmd窗體敲入例如以下的指令:
wsimport -keep -d bin -s src wsdl/HelloService.wsdl |
以上是産生同步client的指令。
假設要産生異步client指令,須要在project根檔案夾下建一個binding.xml的檔案,内容例如以下:
<?xml version="1.0" encoding="UTF-8"?> <bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" wsdlLocation="wsdl/HelloService.wsdl" xmlns="http://java.sun.com/xml/ns/jaxws"> <bindings node="wsdl:definitions"> <enableAsyncMapping>true</enableAsyncMapping> </bindings> </bindings> |
然後産生client代碼的wsimport指令也會不一樣:
wsimport -keep –b binding.xml -d bin -s src wsdl/HelloService.wsdl |
我們來看異步調用的代碼(同步代碼比異步調用簡單,留給大家自己做練習)
wsimport指令會在eclipseproject的src檔案夾中生成你在調用時所用的java src檔案。
Hello.java與HelloService.java是wsimport給我們生成的供client調用的java檔案。
我們把HelloService檔案打開,能夠看到兩行:
file:/D:/workspace/JaxWSClient/wsdl/HelloService.wsdl |
把它們改成:
http://localhost:9090/JaxWSSample/HelloService?wsdl |
有兩行,尤其是Url url=這一行,千萬不要漏改了。
我們建立一個調用類,叫:HelloAsyncPollingClient.java檔案,内容例如以下:
package ctsjavacoe.ws.fromjava; import javax.xml.ws.Response; public class HelloAsyncPollingClient { /** * @param args */ public static void main(String[] args) throws Exception { HelloService service = new HelloService(); Hello port = service.getHelloPort(); Response<SayResponse> sayAsync = port.sayAsync("Mk"); while (!sayAsync.isDone()) { System.out.println("is not down"); } try { SayResponse callNameResponse = sayAsync.get(); String message = callNameResponse.getReturn(); System.out.println(message); } catch (Exception ex) { } } } |
執行,得到結果例如以下:
3.3.3 細說同步與異步
在舊的基于JAX-RPC的webservice程式設計model中,是不支援異步的service 調用的,在最新的Jax-ws webservice 程式設計model中,增加了對webservice的異步調用的支援。
首先我來講一下它的原理,大家不要以為在異步的調用下,從client到server 之間的soap message 流也是異步的,事實上不是,Soap/Http 協定在同步跟異步的調用下是一樣的,都是client的service在執行時打開一個connectin,發送請求,然後接收傳回,這些都在同一個connection中。這種方式對我們有什麼影響呢?從client程式的角度來講,沒有影響,client的程式設計模型是由WSDL中的messages跟port types 來定義的,僅僅要這些東西沒有改變,request 跟response是不是在同一個Tcp/ip 的session 中來發送對與我們來說沒由影響,然後從架構跟資源的角度來講,對我們的影響就大了,把連接配接層的資源跟應用層的程式執行狀态綁定起來是由很多弊端的,假如在異步調用時,程式執行出現了異常,将會導緻連接配接層的資源被一直占用,這樣會極大的影響我們程式的,穩定性,可靠性,資源的使用跟性能。
3.3.4 異步的還有一種實作
上例中實作的是一種“polling方式的異步調用”,以下給出“callback”方式的異步調用client。
由于此callBack當請求發出去以後目前的這個connection就會關閉 ,為了達到測試的目的,增加了sleep,讓client程式等待server端得傳回。
callback類型的client要傳入一個javax.xml.ws.AsyncHandler類型的匿名内部類,當soapMessage 到達時,Jax-ws會調handleResponse這種方法來處理response.
client測試代碼例如以下:
package ctsjavacoe.ws.fromjava; import javax.xml.ws.AsyncHandler; import javax.xml.ws.Response; public class HelloAsyncCallBackClient { public static void main(String[] args) throws Exception { HelloService service = new HelloService(); Hello port = service.getHelloPort(); port.sayAsync("Mk", new AsyncHandler<SayResponse>() { public void handleResponse(Response<SayResponse> res) { try { SayResponse response = null; response = res.get(); String message = response.getReturn(); System.out.println(message); } catch (Exception e) { e.printStackTrace(); } } }); Thread.sleep(1000); } } |