jaxws-webservice編程

 隨着近幾年來,SOA,EAI等架構體系的日漸成熟,Webservice越來越熾手可熱,尤其是在企業做異質平臺整合時成爲了首選的技術。

    Java的Webservice技術更是層出不窮,比較流行的有:Axis2,Spring WS以及Jaxws。

    本人在日常工作和以往工程中,在使用了上述這些Webservice後進行了總結,比較,最終覺得jaxws是目前最標準,需要額外第三方插件最少,配置最少最靈活的webservice。

    JAXWS適合幾乎所有Webservice客戶端的調用,因此不少巨頭型的廠商如:IBM,Weblogic等,在他們的產品上都使用了以JAXWS爲標準的Webservice接口。

本教程分成五天,爲初級教程。

    通過本教程,可以使一個沒有Webservice概念或者沒有寫過Webservice的JAVA Resource快速上手入門,並能滿足一般中小型項目中Webservice的應用。

對於Webservice Security,在(初級)教程中並不提供,會在高級教程中詳細描述。

    不過真正利用Webservice Security特性即XML加密技術的工程並不多,少之又少,大多還是以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特性

1、支持SOAP 1.1(默認)、1.2

2、支持XML/HTTP Binding

3、支持WS-Addressing

4、支持document/literal樣式

5、支持WS-I Basic Profile 1.1

6、支持消息傳輸優化機制(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安裝註解

【轉者注:jdk1.7_X也可以】

JAX-WS RI組件下載後爲一個”.jar”文件,它並不能直接在工程中使用,它是一個以JAVA Swing爲界面的JAXWS的安裝程序包。

我們需要打開一個命令行窗口,並輸入:


輸入完這條命令後,你會得到一個安裝界面如下:

下一步,下一步完成安裝後你會得到這樣的一個目錄:

這個目錄裏有我們寫JAXWS所需的所有lib包以及JAXWS自帶的教程。

三、一切始於HelloWorld

3.1建立Webservice的Server端工程


【轉者注:在Eclipse新建Dynamic Web Project,之後wsdl和wssrc文件夾自己添加】

可以看到我們這個目錄除傳統的src,WebContent目錄外還有幾個目錄,它們分別是:

?    build

?    wsdl

?    wssrc

我們來書寫我們的第一個Webservice吧,它的名字叫Hello(Come on, 老套了,又來了)。

1
2
3
4
5
6
7
8
9
10
11
12
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。

業務邏輯很簡單,客戶端調用傳入一個Name,服務端返回給客戶端一個”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了,現在要我們做客戶端集成。

因此,我們選用Server端通過Java Class生成webservice,而客戶端通過wsdl生成Java調用類的做法。

JAXWS爲我們提供了兩個工具:

 wsgen:主要用於Server端通過Java類編譯成Webservice及相關的wsdl文件

wsimport:主要用於Client端(調用端)通過wsdl編譯出調用Server端的Java文件

我們就來生成一下上面的這個Hello,打開一個command窗口,鍵入如下命令:


【轉者注:

wsgen -cp F:\WorkSpace2014\WSHelloWorld\WebContent\WEB-INF\classes org.zky.shane.Hello -wsdl -s wssrc -d build -r wsdl

-wsdl參數代表生成webservice

- s參數代表生成的.java文件置於何處

-d 參數代表生成的編譯class文件置於何處(這個可以忽略,我們利用eclipse編譯)

-r 參數代表生成的.wsdl文件與.xsd文件生成在何處

-cp參數代表classpath,即Hello.class的所在,爲什麼我們的-cp後是這麼長一個路徑呢?請看Eclipse裏工程編譯輸出目錄的路徑就知道了:

3.2.1 生成的src文件

好,我們現在回到eclipse工程裏,刷新一下工程:


看到在wssrc目錄下已經生成了我們所需的java文件了,請手工cut(對,是cut)這些文件到我們工程的”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

佈署前的準備:

在工程的WEB-INF目錄下建立“sun-jaxws.xml”文件,內容如下:

1
2
3
4
5
6
<?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,則寫法爲,

1
2
3
4
5
<?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文件,增加如下內容:

1
2
3
4
5
6
7
8
9
<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。

【轉者注:原博文的web.xml缺少了配置listener,下面給出其在web.xml中的配置】

1
2
3
4
5
<listener>
        <listener-class>
            com.sun.xml.ws.transport.http.servlet.WSServletContextListener
        </listener-class>
    </listener>

開始佈署:

1.  在tomcat的webapps目錄下建立一個目錄叫“D:\tomcat2\webapps\JaxWSSample”

2.  把eclipse工程JaxWSSample下WebContent目錄下所有的東西copy至該目錄下

【轉者注:需要把jax-ws相關jar添加到lib中】

3.  重啓tomcat

在ie中輸入:

http://localhost:9090/JaxWSSample/HelloService?wsdl

可以看到我們的webservice已經生成了。

轉者注:根據個人機器的tomcat或其他web容器設定端口號

3.3通過Server端的WSDL生成供JAVA調用的客戶端

3.3.1 同步與異步

同步調用,很好理解,即一來一回,Client端request到Server端,Sever端立刻回一個response。

異步調用,就是客戶端調用一次服務端後,服務端處理事務並不是即時返回的,比如說傳一個600MB文件給服務端,服務端在處理接收和解析文件時,客戶端不會馬上得到一個響應,它會等待一段時間,等服務器處理完後,再通知客戶端“我處理完了”。

3.3.2 利用wsimport產生客戶端

我們新建一個eclipse的工程,只需要是JAVA工程就行了,不需要web工程的,因爲我們這邊只用代碼做調用:


把Server端的wsdl及xsd都手工copy到客戶端工程的wsdl目錄下。

打開一個cmd窗口敲入如下的命令:

1
wsimport -keep -d bin -s src wsdl/HelloService.wsdl

以上是產生同步客戶端的命令。

如果要產生異步客戶端命令,需要在工程根目錄下建一個binding.xml的文件,內容如下:

1
2
3
4
5
6
7
8
<?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>

然後產生客戶端代碼的wsimport命令也會不一樣:

1
wsimport -keep –b binding.xml  -d bin -s src wsdl/HelloService.wsdl

我們來看異步調用的代碼(同步代碼比異步調用簡單,留給大家自己做練習)

wsimport命令會在eclipse工程的src目錄中生成你在調用時所用的java src文件。

Hello.java與HelloService.java是wsimport給我們生成的供客戶端調用的java文件。

我們把HelloService文件打開,可以看到兩行:

1
file:/D:/workspace/JaxWSClient/wsdl/HelloService.wsdl

把它們改成:

1
http://localhost:9090/JaxWSSample/HelloService?wsdl

有兩行,尤其是Url url=這一行,千萬不要漏改了。

我們創建一個調用類,叫:HelloAsyncPollingClient.java文件,內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 協議在同步跟異步的調用下是一樣的,都是客戶端的service在運行時打開一個connectin,發送請求,然後接收返回,這些都在同一個connection中。這種方式對我們有什麼影響呢?從客戶端程序的角度來講,沒有影響,客戶端的編程模型是由WSDL中的messages跟port types 來定義的,只要這些東西沒有改變,request 跟response是不是在同一個Tcp/ip 的session 中來發送對與我們來說沒由影響,然後從架構跟資源的角度來講,對我們的影響就大了,把連接層的資源跟應用層的程序運行狀態綁定起來是由許多弊端的,假如在異步調用時,程序運行出現了異常,將會導致連接層的資源被一直佔用,這樣會極大的影響我們程序的,穩定性,可靠性,資源的使用跟性能。

3.3.4 異步的另一種實現

上例中實現的是一種“polling方式的異步調用”,下面給出“callback”方式的異步調用客戶端。

由於此callBack當請求發出去以後當前的這個connection就會關閉 ,爲了達到測試的目的,加入了sleep,讓客戶端程序等待服務器端得返回。

callback類型的client要傳入一個javax.xml.ws.AsyncHandler類型的匿名內部類,當soapMessage 到達時,Jax-ws會調handleResponse這個方法來處理response.

客戶端測試代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章