在以前的文章中,涉及到了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; } }
不須要任何額外的命令,依賴,以及部署,即可直接運行上面的程序。