java創建webservice client


java的webservice實現有多種方式,可用的工具也有一些。之前對這塊的只是比較缺乏,以至於一上來就一直看spring webservice.花費了幾天后發現和要用的功能不符,就···

當前學習的需求是webservice client。因此整篇文章用來說明java webserviceclient的創建過程。

首先使用java自帶的soapconnection實現。那首先具體的client訪問流程為

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.我采用的方法是

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對象。接下來是轉換方式

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向服務端發消息了。

那消息發送出去服務端返回結果是不是還要處理一下呢?當然可以通過元素逐級獲取的方式獲取你要的元素。同樣擴展性太差。我采用的方式同樣是把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的方式

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中各個元素的名字。

呼~ 終於走完了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM