在以前的文章中,涉及到了WebService的搭建。全部的EndPoint均是高度面向对象,面向逻辑了。Server与Client之间交互的消息,均由JAXB转为JAVA类型。若是想对消息的原始数据进行修改,可使用Handler Chain。java
然而,JAXWS也提供了另外一种编程方式,Provider和Dispatch,让咱们抛开高度抽象的EndPoint和JAXB,抛开了工具wsgen,wsimport,直接面向消息编程。node
Provider是server端直接面向消息编程的接口。咱们先看下Provider接口中的方法:
web
package javax.xml.ws.Provider public interface Provider<T> { public T invoke(T request); }
服务端EndPoint必须实现此接口。Provider就像是HttpServlet,invoke()就像是service()。invoke方法参数是接收的原始消息,返回值是返回的消息。编程
T是对消息封装的一种泛型。结合Provider的Annotation @ServiceMode,它能够为三种封装类型:tomcat
javax.xml.transform.Source 将消息中的Payload封装为XML类型Source。适用于@ServiceMode(value=Service.Mode.PAYLOAD)。服务器
javax.xml.soap.SOAPMessage 将消息总体封装为SOAPMessage。适用于@ServiceMode(value=Service.Mode.MESSAGE)。dom
javax.activation.DataSource异步
Provider没法使用wsgen命令生成WSDL文件,因此,只能先用WebService Interface生成WSDL,而后再编写Provider。
ide
下面的例子是基于以前的图书馆系统而修改的,因此复用以前图书馆系统的WSDL文件。工具
建立一个java web project,取名叫LibraryProvider。而后将以前图书馆系统的WSDL文件复制到新项目的WEB-INF目录下面。
建立java类,LibraryProvider:
@ServiceMode(value=Service.Mode.PAYLOAD) @WebServiceProvider() public class LibraryProvider implements Provider<Source> { private static int currentId = 0; private static Map<Integer, Book> books= new HashMap<Integer, Book>(); private static class Book { public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } private int id; private String name; private String author; } @Override public Source invoke(Source request) { try { DOMResult dom = new DOMResult(); Transformer trans = TransformerFactory.newInstance().newTransformer(); trans.transform(request, dom); Node node = dom.getNode(); Node root = node.getFirstChild(); String operation = root.getLocalName(); if ("addRawBook".equals(operation)) { return addRawBook(root); } if ("getRawBook".equals(operation)) { return getRawBook(root); } if ("deleteBook".equals(operation)) { return deleteBook(root); } return request; } catch(Exception e) { e.printStackTrace(); throw new RuntimeException("Error in provider endpoint", e); } } private Source addRawBook(Node root) { String name = root.getChildNodes().item(0).getChildNodes().item(0).getNodeValue(); String author = root.getChildNodes().item(1).getChildNodes().item(0).getNodeValue(); Book b = new Book(); b.setName(name); b.setAuthor(author); b.setId(++currentId); books.put(b.getId(), b); String body = "<ns2:addRawBookResponse xmlns:ns2=\"http://library.mycompany.com\"><id>" +currentId +"</id></ns2:addRawBookResponse>"; Source source = new StreamSource(new ByteArrayInputStream(body.getBytes())); return source; } private Source getRawBook(Node root) { String idString = root.getChildNodes().item(0).getChildNodes().item(0).getNodeValue(); Book b = books.get(Integer.parseInt(idString)); String body = "<ns2:getRawBookResponse xmlns:ns2=\"http://library.mycompany.com\"><rawBook>" +b.toString() +"</rawBook></ns2:getRawBookResponse>"; Source source = new StreamSource(new ByteArrayInputStream(body.getBytes())); return source; } private Source deleteBook(Node root) { // 略 } }
在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="provider" implementation="com.mycompany.library.LibraryProvider" wsdl="WEB-INF/wsdl/LibraryService.wsdl" service="{http://library.mycompany.com}LibraryService" port="{http://library.mycompany.com}LibraryPort" url-pattern="/service" /> </endpoints>
将project导出为war包library.war,部署到tomcat下。访问http://127.0.0.1:8080/library/service?wsdl
而后使用wsimport,产生client端的代码。对client端进行调用,查看结果。
关于提供服务端异步执行功能的AsyncProvider,我会另写一篇文章介绍。
接口Dispatch和Provider相对,用于client端。其功能与Provider同样,提供了面向消息的编程方法。先看下Dispatch提供了哪些方法:
package javax.xml.ws; import java.util.concurrent.Future; public interface Dispatch<T> extends BindingProvider { public T invoke(T msg); public Response<T> invokeAsync(T msg); public Future<?> invokeAsync(T msg, AsyncHandler<T> handler); public void invokeOneWay(T msg); }
T泛型与Provider中的T同样。而方法invoke与Provider中的invoke也相同。Dispatch还提供了另外3个方法:
Response<T> invokeAsync(T msg); 为异步poll的方式, Response是一个Future。
Future<?> invokeAsync(T msg, AsyncHandler<T> handler); 为异步callback方式。Handler会在另外一个线程中处理返回的值。
void invokeOneWay(T msg); 处理单向消息。
关于异步,会在另外一篇文章中赞成描述。
因为Dispatch摆脱了JAXB,因此不须要调用wsimport命令。只须要一个简单的client程序,就能够调用服务器端的service。咱们以图书馆webservice为例。
public class Client { public static void main(String[] a) throws Exception { URL url = new URL("http://127.0.0.1:8080/library/service?wsdl"); final QName serviceQName = new QName("http://library.mycompany.com", "LibraryService"); Service service = Service.create(url, serviceQName); // 建立一个新的port,也可使用WSDL中已有的port。若是WSDL中已经有此port,则不须要再进行binding。 QName portName = new QName("http://library.mycompany.com", "RandomPort"); service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING,"http://127.0.0.1:8080/library/service"); // 为port建立一个dispatch。全部流向port的消息都将由dispatch处理。 Dispatch<Source> sourceDispatch = service.createDispatch(portName, Source.class, Service.Mode.PAYLOAD); // add book String body = "<ns2:addRawBook xmlns:ns2=\"http://library.mycompany.com\"><name>java</name><author>xpbug</author></ns2:addRawBook>"; Source result = sourceDispatch.invoke(new StreamSource(new StringReader(body))); System.out.println(sourceToXMLString(result)); } private static String sourceToXMLString(Source result) { String xmlResult = null; try { TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); OutputStream out = new ByteArrayOutputStream(); StreamResult streamResult = new StreamResult(); streamResult.setOutputStream(out); transformer.transform(result, streamResult); xmlResult = streamResult.getOutputStream().toString(); } catch (TransformerException e) { e.printStackTrace(); } return xmlResult; } }
不须要任何额外的命令,依赖,以及部署,即可直接运行上面的程序。