java的webservice實現有多種方式,可用的工具也有一些。以前對這塊的只是比較缺少,以致於一上來就一直看spring webservice.花費了幾天後發現和要用的功能不符,就···java
當前學習的需求是webservice client。所以整篇文章用來講明java webserviceclient的建立過程。web
首先使用java自帶的soapconnection實現。那首先具體的client訪問流程爲spring
SOAPConnection connection = null; try { SOAPConnectionFactory sfc = SOAPConnectionFactory.newInstance(); connection = sfc.createConnection(); SOAPMessage soapMessage = ObjectToSoapXml(object, nsMethod, nsName); URL endpoint = new URL(new URL(url), "", new URLStreamHandler() { @Override protected URLConnection openConnection(URL url) throws IOException { URL target = new URL(url.toString()); URLConnection connection = target.openConnection(); // Connection settings connection.setConnectTimeout(120000); // 2 min connection.setReadTimeout(60000); // 1 min return(connection); } }); SOAPMessage response = connection.call(soapMessage, endpoint); } catch (Exception e) { }
這其中首先建立soapconnection調用call方法向server端發送請求,call的兩個參數一個是發送的消息soapmessage,一個是服務器端地址。服務器
那這裏的關鍵是soapmessage的封裝,那在java中,信息通常採用對象的形式存儲。問題就是怎樣把含有信息的對象封裝成soapMessage.我採用的方法是app
private static<T> SOAPMessage ObjectToSoapXml(T object, String nsMethod, String nsName) { SOAPMessage soapMessage = null; try { MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); soapMessage = messageFactory.createMessage(); SOAPPart soapPart = soapMessage.getSOAPPart(); // SOAP Envelope SOAPEnvelope envelope = soapPart.getEnvelope(); envelope.setPrefix("SOAP-ENV"); envelope.addNamespaceDeclaration("ns1", nsMethod); // SOAP Body SOAPBody soapBody = envelope.getBody(); soapBody.setPrefix("SOAP-ENV"); soapBody.addDocument(jaxbObjectToXML(object, nsMethod, nsName));//將body中的類經過document的形式寫入 soapMessage.saveChanges(); } catch (SOAPException e) { e.printStackTrace(); } return soapMessage; }
使用messagefactory建立soapmessage,這裏有一點要注意SOAPConstants.SOAP_1_1_PROTOCOL,使用這個參數的緣由是要指定soapmessage的content-type爲text/xml,charset=utf-8,那其餘的可再參考其餘常量。那建立的soapmessage就包含soapenvelop了,可添加前綴和命名空間。接下來就是在soapbody中添加要傳遞的信息對象。一開始看到的各類例子都是一個元素一個元素的添加。可擴展性太差了。一直考慮將整個對象添加進去。採用方式是soapbody adddocument的方式。那就要把信息對象轉換成org.w3c.dom.Document對象。接下來是轉換方式dom
private static<T> Document jaxbObjectToXML(T emp, String nsMethod, String nsName) { try { JAXBContext context = JAXBContext.newInstance(emp.getClass()); // Create the Document DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.newDocument(); // Marshal the Object to a Document Marshaller marshaller = context.createMarshaller(); marshaller.marshal(emp, document); if(null != document) { document.renameNode(document.getFirstChild(), nsMethod, nsName); } return document; } catch (Exception e) { logger.error(e.toString(), e); } return null; }
使用jaxb將object轉成xml,marshal方法可直接實現對象到document。我在這裏遇到的一個問題是,對象到xml的時候,個人xml要求根元素有前綴。沒知識實在很差添加。最終找到實現方式是轉換成的document獲取根元素,經過getfirstchild的方式,而後對根元素重命名 renameNode,這裏的問題是這個方法的後兩個參數一個是命名空間,一個是重命名後節點名稱。我要使用的是含有前綴,無命名空間。其實這樣說就是沒知識了。前綴和命名空間應該是對應的,命名空間和前綴應一塊兒設置。只是命名空間並不顯示。若只設置前綴,無命名空間則會報錯。那這裏問題就愉快的解決了,此時是完成了對象封裝成soapmessage,就能夠經過soapconnection向服務端發消息了。ide
那消息發送出去服務端返回結果是否是還要處理一下呢?固然能夠經過元素逐級獲取的方式獲取你要的元素。一樣擴展性太差。我採用的方式一樣是把soapbody中的內容實現到對象的對應。工具
public static<T> T parseSoapMessage(SOAPMessage reqMsg, T object, String name) { try { reqMsg = removeUTFBOM(reqMsg); SOAPBody soapBody = reqMsg.getSOAPBody(); Document document = soapBody.extractContentAsDocument();//獲取返回信息中的消息體 document.renameNode(document.getFirstChild(), null, name);//根節點去掉前綴 JAXBContext jc = JAXBContext.newInstance(object.getClass()); Unmarshaller unmarshaller = jc.createUnmarshaller(); object = (T)unmarshaller.unmarshal(document); }catch(Exception e) { logger.error(e.toString(), e); } return object; }
大問題來了,調用soapconnection返回的soapmessage,直接調用getsoapbody報錯了。幾番查看,是返回的結果是utf-8 BOM滴,額,這個處理沒有找到好的方式。最終也只是將soapmessage轉成string將BOM去掉以後再轉換回來。因以前對象到soapmessage轉換時使用document,那如今也考慮這種方式而且可行了。那注意抽取出來的document呢我這邊仍是含有前綴的,因此使用renameNode作了一下去除前綴的處理,而後使用unmarshal將document嗨皮的轉成對象了。終於完成了。學習
去BOM的方式ui
private static SOAPMessage removeUTFBOM(SOAPMessage soapMessage) { ByteArrayOutputStream baos = null; try { baos = new ByteArrayOutputStream(); soapMessage.writeTo(baos); String soapString = baos.toString(); if (baos.toString().startsWith("\uFEFF")) { soapString = soapString.substring(1); InputStream is = new ByteArrayInputStream(soapString.getBytes()); soapMessage = MessageFactory.newInstance().createMessage(null, is); } } catch (SOAPException e) { logger.error(e.toString(), e); } catch (IOException e) { logger.error(e.toString(), e); } return soapMessage; }
最後還有一點就是xml對應bean的定義
我採起的方式是在類上註解
@XmlRootElement(name = "name") //聲明爲根元素,根元素名字
@XmlAccessorType(XmlAccessType.FIELD)
而後在各個元素上
@XmlElement(name = "elementname", nillable = true)
指定該屬性對應xml中元素的名字,使用nillable屬性是由於,若此屬性爲空的話,相應的xml元素便不存在,指定此屬性爲true,則爲空的屬性也會顯示。
再就是爲根元素的類中含有其餘對象時,其餘對象的聲明方式
首先在類上聲明 @XmlAccessorType(XmlAccessType.FIELD)
相應屬性上聲明 @XmlElement(name = "elementname", nillable = true)
再就是有些屬性爲list
@XmlElementWrapper(name="ListName")
@XmlElement(name="ListelementName", nillable = true)
wrapper指定list的名字,接下來指定list中各個元素的名字。
呼~ 終於走完了