java建立webservice client

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

呼~ 終於走完了

相關文章
相關標籤/搜索