WebService最外层的包装其实是一个com.sun.xml.ws.transport.http.servlet.WSServlet。根据servlet的生命周期,每一个新的request,都是一个独立的线程。线程之间是无联系的,除了statful,statful将在后面文章中描述。
在每个request都是独立线程的情况下,server端的异步并不是很必要。但JAXWS还是提供了server端异步的机制。
在client端,异步是很重要的,就像浏览器在加载页面的时候,会异步的加载JS,CSS和图片一样。异步是提高客户端性能的重要手段。
前提
在理解JAXWS异步机制之前,必须要先了解Provider, Dispatch和Customization. 这三部分均可以再前面的文章中找到。
概括
在JAXWS中,异步的实现有3种方式:
只针对Server端,使用AsyncProvider.
只针对Client端,使用Dispatch。
只针对Client端,使用Customization产生异步service port operation。
前两种方法使用的是面向消息编程。AsyncProvider与Provider类似,只是AsyncProvider提供了一个异步方法,使用callback的方式异步返回消息。 Dispatch则提供了两个异步方法,一个是使用Future polling的方式,一个是使用callback方式。
第3中方式则是针对面向逻辑,面向对象的Service Port。使用wsimport命令,将WSDL+customization转成支持持异步的Web Service client。
下面,我将上一篇文章中的图书馆系统的client端,改造其中的一个addBook方法成异步方法。
创建customization文件
基于前一篇文章之中图书馆系统,其服务地址为http://localhost:8080/library/service
我们使用wsimport来创建客户端,在创建客户端之前,必须先创建customization,以使addBook operation能转为异步方法。
custom.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
wsdlLocation="http://localhost:8080/library/service?wsdl=1"
xmlns="http://java.sun.com/xml/ns/jaxws">
<enableWrapperStyle>true</enableWrapperStyle>
<enableAsyncMapping>false</enableAsyncMapping>
<!-- wsdl:portType operation customization -->
<bindings node="wsdl:definitions/wsdl:portType[@name='Library']/wsdl:operation[@name='addBook']">
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
</bindings>
生成客户端
因为Project是用Maven创建,所以wsimport用的是maven中的插件。
<plugin>
<groupId>org.jvnet.jax-ws-commons</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>wsimport-from-jdk</id>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<executable>${tool.wsimport}</executable>
<wsdlUrls>
<wsdlUrl>http://localhost:8080/library/service?wsdl</wsdlUrl>
</wsdlUrls>
<bindingFiles>
<bindingFile>d:/share/custom.xml</bindingFile>
</bindingFiles>
<verbose>true</verbose>
<xdebug>true</xdebug>
</configuration>
</execution>
</executions>
</plugin>
运行mvn generate-sources
将产生的java文件复制到项目src中。
打开Library.java文件,发现addBook产生了3个方法:
/**
*
* @param book
* @return
* returns javax.xml.ws.Response<com.mycompany.library.AddBookResponse>
*/
@WebMethod(operationName = "addBook")
@RequestWrapper(localName = "addBook", targetNamespace = "http://library.mycompany.com", className = "com.mycompany.library.AddBook")
@ResponseWrapper(localName = "addBookResponse", targetNamespace = "http://library.mycompany.com", className = "com.mycompany.library.AddBookResponse")
public Response<AddBookResponse> addBookAsync(
@WebParam(name = "book", targetNamespace = "http://library.mycompany.com")
Book book);
/**
*
* @param book
* @param asyncHandler
* @return
* returns java.util.concurrent.Future<? extends java.lang.Object>
*/
@WebMethod(operationName = "addBook")
@RequestWrapper(localName = "addBook", targetNamespace = "http://library.mycompany.com", className = "com.mycompany.library.AddBook")
@ResponseWrapper(localName = "addBookResponse", targetNamespace = "http://library.mycompany.com", className = "com.mycompany.library.AddBookResponse")
public Future<?> addBookAsync(
@WebParam(name = "book", targetNamespace = "http://library.mycompany.com")
Book book,
@WebParam(name = "asyncHandler", targetNamespace = "")
AsyncHandler<AddBookResponse> asyncHandler);
/**
*
* @param book
* @return
* returns com.mycompany.library.Book
*/
@WebMethod
@WebResult(name = "result", targetNamespace = "http://library.mycompany.com")
@RequestWrapper(localName = "addBook", targetNamespace = "http://library.mycompany.com", className = "com.mycompany.library.AddBook")
@ResponseWrapper(localName = "addBookResponse", targetNamespace = "http://library.mycompany.com", className = "com.mycompany.library.AddBookResponse")
@Action(input = "http://library.mycompany.com/Library/addBookRequest", output = "http://library.mycompany.com/Library/addBookResponse")
public Book addBook(
@WebParam(name = "book", targetNamespace = "http://library.mycompany.com")
Book book);
其中addBook是同步方法,addBookAsync是异步方法,两个addBookAsync中,一个是Future Poll模式,另一个是callback模式。
使用异步方法
在App.java的main函数中,演示一下异步调用的使用:
public class App {
public static void main( String[] args ) throws InterruptedException, ExecutionException {
Library port = new LibraryImplService().getLibraryImplPort();
// polling
Book newBook = new Book();
newBook.setAuthor("xpbug");
newBook.setName("java");
Response<AddBookResponse> response = port.addBookAsync(newBook);
while (!response.isDone()) {
Thread.sleep(1000);
}
printBook(response.get().getResult());
// callback
newBook = new Book();
newBook.setAuthor("cat");
newBook.setName("c++");
port.addBookAsync(newBook, new CallbackHandler());
// wait 8 seconds then exit program.
Thread.sleep(8000);
}
public static void printBook(Book book) {
System.out.println("[id="+book.getId()+"; name="+book.getName()+"; author="+book.getAuthor()+"]");
}
private static class CallbackHandler implements AsyncHandler<AddBookResponse> {
public void handleResponse(Response<AddBookResponse> response) {
try {
printBook(response.get().getResult());
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}