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方法成异步方法。
基于前一篇文章之中图书馆系统,其服务地址为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(); } } } }