1. 程式人生 > >CXF入門教程(5) -- webService非同步呼叫模式

CXF入門教程(5) -- webService非同步呼叫模式

除了教程(3)中介紹的常見的同步呼叫模式,CXF還支援如下兩種形式的非同步呼叫模式:


  • 輪詢方法(Polling approach) - 這種情況下呼叫遠端方法,我們可以呼叫一個特殊的方法;該方法沒有輸出引數,但是返回一個 javax.xml.ws.Response 例項。可以輪詢該 Response 物件(繼承自 javax.util.concurrency.Future 介面)來檢查是否有應答訊息到達。
  • 回撥方法(Callback approach) -這種情況下呼叫遠端方法,我們呼叫另外一個特殊的方法:該方法使用一個回撥物件(javax.xml.ws.AsyncHandler型別)的引用作為一個引數。只要有應答訊息到達客戶端,CXF執行時就會回撥該 AsyncHandler
     物件,並將應答訊息的內容傳給它。

下面是兩種非同步呼叫的方法的描述和示例程式碼。

非同步呼叫示例使用的契約

下面展示的是非同步呼叫示例中使用的WSDL契約,為保證教程的連續性,本文使用的是前面教程(1)中生成的HelloWorld服務的WSDL契約

<?xml version="1.0" ?>
<wsdl:definitions name="HelloWorld"
	targetNamespace="http://service.server.cxf.test.neareast.com/"
	xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<wsdl:types>
		<xs:schema attributeFormDefault="unqualified"
			elementFormDefault="unqualified" targetNamespace="http://service.server.cxf.test.neareast.com/"
			xmlns:tns="http://service.server.cxf.test.neareast.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
			<xs:element name="IntegerUserMap" type="tns:IntegerUserMap"></xs:element>
			<xs:complexType name="User">
				<xs:sequence>
					<xs:element minOccurs="0" name="name" type="xs:string"></xs:element>
				</xs:sequence>
			</xs:complexType>
			<xs:complexType name="IntegerUserMap">
				<xs:sequence>
					<xs:element maxOccurs="unbounded" minOccurs="0" name="entry"
						type="tns:IdentifiedUser"></xs:element>
				</xs:sequence>
			</xs:complexType>
			<xs:complexType name="IdentifiedUser">
				<xs:sequence>
					<xs:element name="id" type="xs:int"></xs:element>
					<xs:element minOccurs="0" name="user" type="tns:User"></xs:element>
				</xs:sequence>
			</xs:complexType>
			<xs:element name="sayHi" type="tns:sayHi"></xs:element>
			<xs:complexType name="sayHi">
				<xs:sequence>
					<xs:element minOccurs="0" name="text" type="xs:string"></xs:element>
				</xs:sequence>
			</xs:complexType>
			<xs:element name="sayHiResponse" type="tns:sayHiResponse"></xs:element>
			<xs:complexType name="sayHiResponse">
				<xs:sequence>
					<xs:element minOccurs="0" name="return" type="xs:string"></xs:element>
				</xs:sequence>
			</xs:complexType>
			<xs:element name="sayHiToUser" type="tns:sayHiToUser"></xs:element>
			<xs:complexType name="sayHiToUser">
				<xs:sequence>
					<xs:element minOccurs="0" name="arg0" type="tns:User"></xs:element>
				</xs:sequence>
			</xs:complexType>
			<xs:element name="sayHiToUserResponse" type="tns:sayHiToUserResponse"></xs:element>
			<xs:complexType name="sayHiToUserResponse">
				<xs:sequence>
					<xs:element minOccurs="0" name="return" type="xs:string"></xs:element>
				</xs:sequence>
			</xs:complexType>
			<xs:element name="getUsers" type="tns:getUsers"></xs:element>
			<xs:complexType name="getUsers">
				<xs:sequence></xs:sequence>
			</xs:complexType>
			<xs:element name="getUsersResponse" type="tns:getUsersResponse"></xs:element>
			<xs:complexType name="getUsersResponse">
				<xs:sequence>
					<xs:element minOccurs="0" name="return" type="tns:IntegerUserMap"></xs:element>
				</xs:sequence>
			</xs:complexType>
		</xs:schema>
	</wsdl:types>
	<wsdl:message name="getUsers">
		<wsdl:part element="tns:getUsers" name="parameters">
		</wsdl:part>
	</wsdl:message>
	<wsdl:message name="sayHi">
		<wsdl:part element="tns:sayHi" name="parameters">
		</wsdl:part>
	</wsdl:message>
	<wsdl:message name="sayHiToUser">
		<wsdl:part element="tns:sayHiToUser" name="parameters">
		</wsdl:part>
	</wsdl:message>
	<wsdl:message name="sayHiToUserResponse">
		<wsdl:part element="tns:sayHiToUserResponse" name="parameters">
		</wsdl:part>
	</wsdl:message>
	<wsdl:message name="sayHiResponse">
		<wsdl:part element="tns:sayHiResponse" name="parameters">
		</wsdl:part>
	</wsdl:message>
	<wsdl:message name="getUsersResponse">
		<wsdl:part element="tns:getUsersResponse" name="parameters">
		</wsdl:part>
	</wsdl:message>
	<wsdl:portType name="iHelloWorld">
		<wsdl:operation name="sayHi">
			<wsdl:input message="tns:sayHi" name="sayHi">
			</wsdl:input>
			<wsdl:output message="tns:sayHiResponse" name="sayHiResponse">
			</wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="sayHiToUser">
			<wsdl:input message="tns:sayHiToUser" name="sayHiToUser">
			</wsdl:input>
			<wsdl:output message="tns:sayHiToUserResponse" name="sayHiToUserResponse">
			</wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="getUsers">
			<wsdl:input message="tns:getUsers" name="getUsers">
			</wsdl:input>
			<wsdl:output message="tns:getUsersResponse" name="getUsersResponse">
			</wsdl:output>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="HelloWorldSoapBinding" type="tns:iHelloWorld">
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>
		<wsdl:operation name="sayHi">
			<soap:operation soapAction="" style="document"></soap:operation>
			<wsdl:input name="sayHi">
				<soap:body use="literal"></soap:body>
			</wsdl:input>
			<wsdl:output name="sayHiResponse">
				<soap:body use="literal"></soap:body>
			</wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="sayHiToUser">
			<soap:operation soapAction="" style="document"></soap:operation>
			<wsdl:input name="sayHiToUser">
				<soap:body use="literal"></soap:body>
			</wsdl:input>
			<wsdl:output name="sayHiToUserResponse">
				<soap:body use="literal"></soap:body>
			</wsdl:output>
		</wsdl:operation>
		<wsdl:operation name="getUsers">
			<soap:operation soapAction="" style="document"></soap:operation>
			<wsdl:input name="getUsers">
				<soap:body use="literal"></soap:body>
			</wsdl:input>
			<wsdl:output name="getUsersResponse">
				<soap:body use="literal"></soap:body>
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="HelloWorld">
		<wsdl:port binding="tns:HelloWorldSoapBinding" name="HelloWorldImplPort">
			<soap:address location="http://localhost:9000/helloWorld"></soap:address>
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>

生成非同步 stub 程式碼

非同步呼叫需要額外的stub程式碼(例如,服務端點介面中定義的專用的非同步方法)。然而,這些特殊的stub程式碼不是預設生成的。要想開啟非同步特性,並生成必不可少的stub程式碼,我們必須使用WSDL 2.0規範的自定義對映特性。

自定義使我們能夠改變 wsdl2java 工具生成stub程式碼的方式。特別地,它允許我們修改WSDL到Java的對映,並開啟某些特性。在這裡,自定義的作用是開啟非同步呼叫特性。自定義是用一個繫結宣告規定的,該宣告是我們用一個 jaxws:bindings 標籤(jaxws 字首繫結到 

http://java.sun.com/xml/ns/jaxws 名稱空間)定義的。指定一個繫結宣告有兩種可選的方式:

  • 外部繫結宣告 - jaxws:bindings 元素被定義在WSDL契約之外的一個單獨的檔案。生成stub程式碼的時候,我們需要對wsdl2java 工具指定繫結宣告檔案的位置。
  • 嵌入式繫結宣告 - 我們也可以直接把jaxws:bindings 元素嵌入到 WSDL 契約中,把它當做WSDL的擴充套件。在這種情況下,jaxws:bindings 的設定僅對直接的父元素起作用。

本文只考慮第一種方法,即外部繫結宣告。一個打開了非同步呼叫開關的繫結宣告檔案的模版如下所示:

<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
          wsdlLocation="http://localhost:9000/helloWorld?wsdl"
          xmlns="http://java.sun.com/xml/ns/jaxws">
  <bindings node="wsdl:definitions">
    <enableAsyncMapping>true</enableAsyncMapping>
  </bindings>
</bindings>

其中的wsdlLocation指定了該繫結宣告影響的WSDL檔案的位置,可以是本地檔案或一個URL。node節點是一個XPath 值,指定該繫結宣告影響所影響的WSDL契約中的節點。 此處把node設為“wsdl:definitions”,表示我們希望對整個WSDL契約起作用。{jaxws:enableAsyncMapping}} 元素設定為true,用來使能非同步呼叫特性。

如果我們只想對一個埠“iHelloWorld”生成非同步方法,我們可以在前面的繫結宣告中指定<bindings node="wsdl:definitions/wsdl:portType[@name='iHelloWorld']"> 。

接下來我們就可以使用wsdl2java命令來生成相應的帶非同步支援的stub程式碼了。為簡單起見,假設繫結宣告檔案儲存在本地檔案async_binding.xml中,我們可以使用類似下面的命令:

wsdl2java -b async_binding.xml hello_world.wsdl

其中-b 選項用來指定繫結宣告檔案。通過這種方法生成stub程式碼之後,HelloWorld的服務端點介面定義如下:

import java.util.concurrent.Future;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.Response;
import javax.xml.ws.ResponseWrapper;


@WebService(targetNamespace = "http://service.server.cxf.test.neareast.com/", name = "iHelloWorld")
@XmlSeeAlso({ObjectFactory.class})
public interface IHelloWorld {

    @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")
    @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")
    @WebMethod(operationName = "sayHi")
    public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> sayHiAsync(
        @WebParam(name = "text", targetNamespace = "")
        java.lang.String text
    );

    @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")
    @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")
    @WebMethod(operationName = "sayHi")
    public Future<?> sayHiAsync(
        @WebParam(name = "text", targetNamespace = "")
        java.lang.String text,
        @WebParam(name = "asyncHandler", targetNamespace = "")
        AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse> asyncHandler
    );

    @WebResult(name = "return", targetNamespace = "")
    @RequestWrapper(localName = "sayHi", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHi")
    @WebMethod
    @ResponseWrapper(localName = "sayHiResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse")
    public java.lang.String sayHi(
        @WebParam(name = "text", targetNamespace = "")
        java.lang.String text
    );

    @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")
    @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")
    @WebMethod(operationName = "sayHiToUser")
    public Response<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> sayHiToUserAsync(
        @WebParam(name = "arg0", targetNamespace = "")
        com.neareast.test.cxf.asyClient.WSDL2Java.User arg0
    );

    @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")
    @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")
    @WebMethod(operationName = "sayHiToUser")
    public Future<?> sayHiToUserAsync(
        @WebParam(name = "arg0", targetNamespace = "")
        com.neareast.test.cxf.asyClient.WSDL2Java.User arg0,
        @WebParam(name = "asyncHandler", targetNamespace = "")
        AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse> asyncHandler
    );

    @WebResult(name = "return", targetNamespace = "")
    @RequestWrapper(localName = "sayHiToUser", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUser")
    @WebMethod
    @ResponseWrapper(localName = "sayHiToUserResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse")
    public java.lang.String sayHiToUser(
        @WebParam(name = "arg0", targetNamespace = "")
        com.neareast.test.cxf.asyClient.WSDL2Java.User arg0
    );

    @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")
    @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")
    @WebMethod(operationName = "getUsers")
    public Response<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> getUsersAsync();

    @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")
    @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")
    @WebMethod(operationName = "getUsers")
    public Future<?> getUsersAsync(
        @WebParam(name = "asyncHandler", targetNamespace = "")
        AsyncHandler<com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse> asyncHandler
    );

    @WebResult(name = "return", targetNamespace = "")
    @RequestWrapper(localName = "getUsers", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsers")
    @WebMethod
    @ResponseWrapper(localName = "getUsersResponse", targetNamespace = "http://service.server.cxf.test.neareast.com/", className = "com.neareast.test.cxf.asyClient.WSDL2Java.GetUsersResponse")
    public com.neareast.test.cxf.asyClient.WSDL2Java.IntegerUserMap getUsers();
}

除了原來的同步方法(如sayHi方法),sayHi操作的兩個非同步呼叫方法也被同時生成了:

  • 返回值型別為Future<?>,有一個型別為javax.xml.ws.AsyncHandler的額外引數的sayHiAsync()方法 —— 該方法可用於非同步呼叫的回撥方式。
  • 返回值型別為Response<GreetMeSometimeResponse>的sayHiAsync()方法 —— 該方法可用於非同步呼叫的輪詢方式。

回撥方式和輪詢方式的細節將在下面的章節討論。為體現非同步呼叫的特點,筆者修改了教程(1)中Helloworld服務的部分實現,在sayHiToUser()方法中加入了3秒鐘的休眠,並增強了程式碼的魯棒性,改動如下:

	public String sayHiToUser(User user) {
		String retVal = null;
		if(null == user){
			retVal = "Error: user object null !";
		}else{
			try{
				System.out.println("sleep for 3 seconds before return");
				Thread.sleep(3000);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
			
			System.out.println("sayHiToUser called by: " + user.getName());
			users.put(users.size() + 1, user);
			retVal = "Hello " + user.getName();
		}
		return retVal;
	}

實現一個輪詢方式的非同步呼叫客戶端

下面的程式碼演示了非同步傳送操作呼叫的輪詢方式的實現。客戶端是通過特殊的Java方法 _OperationName_Async(本例為sayHiAsync()方法)來呼叫這個操作的,該方法返回一個javax.xml.ws.Response<T> 物件,其中“T”是這個操作的響應訊息的型別(本例中為SayHiResponse型別)。我們可以稍後通過輪詢Response<T> 物件來檢查該操作的響應訊息是否已經到達。 

package com.neareast.test.cxf.asyClient.consumer;

import java.util.concurrent.ExecutionException;

import javax.xml.ws.Response;

import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld;
import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld;
import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiResponse;

public class BasicClientPolling {
	public static void main(String[] args) throws InterruptedException{
		HelloWorld server = new HelloWorld();
		IHelloWorld hello = server.getHelloWorldImplPort();

		Response<SayHiResponse> sayHiResponseResp = hello.sayHiAsync(System.getProperty("user.name"));
		while (!sayHiResponseResp.isDone()) {
			Thread.sleep(100);
		}

		try {
			SayHiResponse reply = sayHiResponseResp.get();
			System.out.println( reply.getReturn() );
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		
	}
}

sayHiAsync()方法呼叫了sayHi操作,將輸入引數傳送到遠端的服務,並返回javax.xml.ws.Response<SayHiResponse> 物件的一個引用。Response 類實現了標準的 java.util.concurrency.Future<T> 介面,該類設計用來輪詢一個併發執行緒執行的任務的產出結果。本質上來說,使用Response物件來輪詢有兩種基本方法:

  • Non-blocking polling(非阻塞輪詢) - 嘗試獲得結果之前,呼叫非阻塞方法Response<T>.isDone()來檢查響應訊息是否到達,例如:
    <pre name="code" class="java">	User u = new User();
    		
    	//非阻塞式輪詢
    	u.setName(System.getProperty("user.name"));
    	Response<SayHiToUserResponse> sayHiToUserResponseResp = hello.sayHiToUserAsync(u);
    	while (!sayHiToUserResponseResp.isDone()) {
    		Thread.sleep(100);
    	}
    		
    	try {
    		//如果沒有前面isDone的檢測,此處就退化為阻塞式輪詢
    		SayHiToUserResponse reply = sayHiToUserResponseResp.get();
    		System.out.println( reply.getReturn() );
    	} catch (ExecutionException e) {
    		e.printStackTrace();
    	}</pre><br>
    <pre></pre>
    <pre></pre>
    <pre></pre>
    <pre></pre>
    <pre></pre>
    <pre></pre>
    <pre></pre>
    
  • Blocking polling(阻塞輪詢) - 立即呼叫Response<T>.get(),阻塞至響應到達(可以指定一個超時時長作為可選項)。例如,輪詢一個響應,超時時長為60s:
    	//阻塞式輪詢
    	u.setName("NearEast");
    	sayHiToUserResponseResp = hello.sayHiToUserAsync(u);
    	try {
    		SayHiToUserResponse reply = sayHiToUserResponseResp.get(5L,java.util.concurrent.TimeUnit.SECONDS);
    		System.out.println( reply.getReturn() );
    	} catch (ExecutionException e) {
    		e.printStackTrace();
    	} catch (TimeoutException e) {
    		e.printStackTrace();
    	}

實現一個回撥方式的非同步呼叫客戶端

發起非同步操作呼叫的另一個可選方法是實現javax.xml.ws.AsyncHandler介面,派生出一個回撥類。回撥類必須實現 handleResponse() 方法,CXF執行時呼叫這個類將響應的到達通知給客戶端。下面的程式碼給出了我們需要實現的 AsyncHandler 介面的輪廓。

The javax.xml.ws.AsyncHandler Interface
package javax.xml.ws;

public interface AsyncHandler<T>
{
  void handleResponse(Response<T> res);
}

本例使用一個測試用的回撥類 SayHiToUserAsyHandler,程式碼如下:

package com.neareast.test.cxf.asyClient.consumer;

import java.util.concurrent.ExecutionException;

import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;

import com.neareast.test.cxf.asyClient.WSDL2Java.SayHiToUserResponse;

public class SayHiToUserAsyHandler implements AsyncHandler<SayHiToUserResponse> {
	SayHiToUserResponse reply = null;

	@Override
	public void handleResponse(Response<SayHiToUserResponse> res) {
		try {
			reply = res.get();
			System.out.println( reply.getReturn() );
		} catch (ExecutionException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public String getResponseText(){
		return reply.getReturn();
	}

}

上述 handleResponse() 的實現只是簡單地獲取響應資料,並把它存放到成員變數reply中。額外的getResponseText() 方法是為了方便地從響應中提煉出主要的輸出引數。

下面的程式碼演示了發起非同步操作呼叫的回撥方法。客戶端通過特定的Java方法 _OperationName_Async()來呼叫相應的操作,該方法使用一個額外的AsyncHandler<T>型別的引數,並返回一個 java.util.concurrency.Future<?> 物件。

package com.neareast.test.cxf.asyClient.consumer;

import java.util.concurrent.Future;

import com.neareast.test.cxf.asyClient.WSDL2Java.HelloWorld;
import com.neareast.test.cxf.asyClient.WSDL2Java.IHelloWorld;
import com.neareast.test.cxf.asyClient.WSDL2Java.User;

public class BasicCallbackClient {
	public static void main(String[] args) throws InterruptedException{
		HelloWorld server = new HelloWorld();
		IHelloWorld hello = server.getHelloWorldImplPort();
		
		User u = new User();
		//非阻塞式輪詢
		u.setName(System.getProperty("user.name"));
		SayHiToUserAsyHandler asyHandler = new SayHiToUserAsyHandler();
		Future<?> res = hello.sayHiToUserAsync(u, asyHandler);
		while (!res.isDone()) {
			Thread.sleep(100);
		}
		
		String reply = asyHandler.getResponseText();
		System.out.println( reply );
		
	}
}

sayHiToUserAsync()方法返回的 Future<?> 物件只是用來檢測一個響應是否已經到達的 —— 例如,通過呼叫response.isDone()來輪詢。響應訊息的值只在回撥物件SayHiToUserAsyHandler 中可得。 

本文配套的完整程式碼已經上傳,包括用到的wsdl契約檔案和繫結宣告檔案;本文涉及的非同步呼叫客戶端的程式碼放在com.neareast.test.cxf.asyClient包下,歡迎下載:http://download.csdn.net/detail/neareast/4421250

本文參考自:http://cxf.apache.org/docs/developing-a-consumer.html

轉載自NearEast