【JAX-WS入門系列】第05章_契約優先開發及隱式聲明頭資訊

來源:互聯網
上載者:User

用戶端和服務端都是Java Project,首先列出服務端代碼


首先是我們自己編寫的//src//META-INF//wsdl//myCalculator.wsdl

 

<?xml version="1.0" encoding="UTF-8"?><!-- 編寫wsdl檔案時,可以通過以下三種封裝樣式來定義開放服務的方法 --><!-- 1)基於Document的wrapped方式,即所有參數都通過<element>來封裝 --><!-- 2)基於Document的unWrapped(Bare)方式,即<message>中傳入的是具體的參數名稱 --><!-- 3)基於RPC的方式 --><!-- 這裡示範的是採用第一種方式來編寫的,這也是比較推薦使用的方式 --><!-- 其中targetNamespace的作用類似於Java中的package --><wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:tns="http://blog.csdn.net/jadyer"targetNamespace="http://blog.csdn.net/jadyer"name="CalculatorServiceImpl"><!-- 定義WebService使用的資料類型 --><wsdl:types><!-- 也可以把下面的元素element及其類型定義放到一個schema檔案中,即*.xsd然後在這裡使用include或者import引入,二者區別是namespace<xsd:schema targetNamespace="http://blog.csdn.net/jadyer"><xsd:import schemaLocation="calculator.xsd" namespace="http://blog.csdn.net/jadyer"/><xsd:include schemaLocation="calculator.xsd"/></xsd:schema> --><!-- 這裡targetNamespace屬性值要和上面的<wsdl:definitions xmlns:tns="" targetNamespace=""/>值一樣 --><xsd:schema targetNamespace="http://blog.csdn.net/jadyer"><!-- 定義一組元素,這裡定義了兩個方法add()和minus() --><!-- name="add"表示定義了一個名為add的元素,type="tns:add"表示add元素的類型是tns命名空間下的'add'類型 --><!-- 但我們並不知道add類型是啥類型,因為並不像string是schema資料類型,所以我們就要定義一個名為add的元素類型 --><!-- 所以我們才在下面定義了一個<xsd:complexType name="add"> --><xsd:element name="add" type="tns:add"/><xsd:element name="addResponse" type="tns:addResponse"/><xsd:element name="minus" type="tns:minus"/><xsd:element name="minusResponse" type="tns:minusResponse"/><xsd:element name="licenseInfo" type="xsd:string"/><!-- <xsd:element name="eleCalculatorException" type="tns:CalculatorException"/><xsd:complexType name="CalculatorException"><xsd:sequence><xsd:element name="message" type="xsd:string"/></xsd:sequence></xsd:complexType> --><!-- 定義元素類型 --><xsd:complexType name="add"><!-- 指明a要出現在b的前面,且只出現一次,即add(int a, int b) --><xsd:sequence><xsd:element name="a" type="xsd:int"/><xsd:element name="b" type="xsd:int"/></xsd:sequence></xsd:complexType><xsd:complexType name="addResponse"><!-- 如果我們想讓add(int a, int b)方法沒有傳回值的話,那麼這裡就可以直接寫成<xsd:sequence/>或不寫 --><xsd:sequence><xsd:element name="addResult" type="xsd:int"/></xsd:sequence></xsd:complexType><xsd:complexType name="minus"><xsd:sequence><xsd:element name="num1" type="xsd:int"/><xsd:element name="num2" type="xsd:int"/></xsd:sequence></xsd:complexType><xsd:complexType name="minusResponse"><xsd:sequence><xsd:element name="minusResult" type="xsd:int"/></xsd:sequence></xsd:complexType></xsd:schema></wsdl:types><!-- 定義操作的資料元素,可比作Java中方法的調用參數 --><!-- <wsdl:message name="MsgCalculatorException"><wsdl:part name="fault" element="tns:eleCalculatorException"/></wsdl:message> --><wsdl:message name="add"><wsdl:part name="add" element="tns:add"/></wsdl:message><wsdl:message name="addResponse"><wsdl:part name="addResponse" element="tns:addResponse"/></wsdl:message><wsdl:message name="minus"><wsdl:part name="minus" element="tns:minus"/></wsdl:message><wsdl:message name="minusResponse"><wsdl:part name="minusResponse" element="tns:minusResponse"/></wsdl:message><wsdl:message name="licenseInfo"><wsdl:part name="licenseInfo" element="tns:licenseInfo"/></wsdl:message><!-- 待綁定的對象介面,即描述了WebService可被執行的操作以及相關的訊息,其可比作Java裡的方法 --><wsdl:portType name="CalculatorService"><!-- 待綁定的add()方法 --><wsdl:operation name="add"><wsdl:input message="tns:add"/><wsdl:output message="tns:addResponse"/></wsdl:operation><!-- 待綁定的minus()方法 --><wsdl:operation name="minus"><wsdl:input message="tns:minus"/><wsdl:output message="tns:minusResponse"/><!-- <wsdl:fault name="CalculatorException" message="tns:MsgCalculatorException"/> --></wsdl:operation></wsdl:portType><!-- 為每個連接埠定義訊息格式和協議細節......這裡type要按照<wsdl:portType>的name來寫 --><wsdl:binding name="CalculatorServiceImplPortBinding" type="tns:CalculatorService"><!-- style屬性可取值'rpc'或'document',而transport屬性定義了要使用的SOAP協議,這裡我們使用HTTP --><!-- soap:binding是採用SOAP1.1版本,soap12:binding是採用SOAP1.2版本,並且是採用SOAP規範來形成HTTPRequest --><soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/><!-- operation元素定義了每個連接埠提供的操作符 --><wsdl:operation name="add"><wsdl:input><!-- use屬性用於指定SOAP訊息的編碼規則,其值為encoded或者literal --><!-- literal意味著type定義遵循xml模式定義 --><!-- encoded參考xml中已有的應用資料,通常指的是SOAP1.1規範中的soap編碼規則,若文檔中無自訂資料,便可選擇encoded --><soap:body use="literal"/></wsdl:input><wsdl:output><soap:body use="literal"/></wsdl:output></wsdl:operation><wsdl:operation name="minus"><wsdl:input><soap:body use="literal"/><!-- 指明訪問minus()方法時需添加頭資訊 --><soap:header use="literal" part="licenseInfo" message="tns:licenseInfo"/></wsdl:input><wsdl:output><soap:body use="literal"/></wsdl:output><!-- <wsdl:fault name="CalculatorException"><soap:fault name="CalculatorException" use="literal"/></wsdl:fault> --></wsdl:operation></wsdl:binding><!-- 這裡的name值要與頂部<wsdl:definitions>標籤的name值一致 --><wsdl:service name="CalculatorServiceImpl"><!-- 這裡binding屬性要與<wsdl:binding>標籤的name值一致,這裡的name值可以自訂 --><wsdl:port binding="tns:CalculatorServiceImplPortBinding" name="CalculatorServiceImplPort"><!-- 這裡用來指定服務發布的地址,可隨意指定 --><soap:address location="http://127.0.0.1:8088/myCalculatorService"/></wsdl:port></wsdl:service></wsdl:definitions>

然後是SIB,即服務端介面實作類別CalculatorServiceImpl.java

 

 

package net.csdn.blog.jadyer;import javax.jws.WebService;/** * SIB(Service Implemention Bean) * @see ---------------------------------------------------------------------------------------------------------- * @see 契約優先的核心 * @see 1)其實WebServices開發服務端的過程中,完全可以不寫SEI,而直接寫一個SIB,然後把服務對外發布,用戶端就足矣正常訪問 * @see   這個時候,也可以在SIB中直接使用@WebParam和@WebResult來指明發布出去的方法中的參數名和傳回值名稱等等 * @see 2)我們寫SEI的目的就是要使用裡面配置的@WebResult等註解,而要讓SEI中的註解生效,就要在SIB中使用endpointInterface * @see   由於SEI是由我們編寫的wsdl檔案產生的,所以它裡面的註解都是很規範的,我們在SIB中只關注實現即可,可以理解為面向介面編程 * @see   所以我們在使用wsdl檔案產生服務端代碼後,就可以把除了SEI外的其它類都刪掉 * @see ---------------------------------------------------------------------------------------------------------- * @see 手工編寫SIB * @see 1)最好顯式的讓SIB和SEI的targetNamespace相同 * @see 2)當指定serviceName值時,其值應為wsdl檔案中的<wsdl:service name="CalculatorServiceImpl">標籤的name值 * @see 3)不指定serviceName值時,wsdl中的<wsdl:service name="">則應為SIBService,如CalculatorServiceImplService * @see   否則在啟動WebService服務時,會報告下面的異常 * @see   Exception in thread "main" javax.xml.ws.WebServiceException: * @see   wsdl file:/F:/Tool/Code/JavaSE/ws_contractFirst/bin/META-INF/wsdl/myCalculator.wsdl * @see   has the following services [{http://blog.csdn.net/jadyer}CalculatorServiceImpl] * @see   but not {http://blog.csdn.net/jadyer}CalculatorServiceImplService * @see   Maybe you forgot to specify a service name in @WebService/@WebServiceProvider? * @see ---------------------------------------------------------------------------------------------------------- * @see 隱式聲明頭資訊 * @see 1)wsdl檔案的<wsdl:input>中增加<soap:header use="...." part="licenseInfo" message="...."/> * @see 2)由於我們在wsdl檔案中聲明的是<soap:header>,所以此時wsdl產生的SEI中是找不到我們所聲明的licenseInfo的 * @see   作為服務端,可以在欲驗證頭資訊的方法中加一個licenseInfo參數,並註解@WebParam(name = "licenseInfo", header=true) * @see   作為用戶端,調用時會發現minus()並不需要傳licenseInfo參數..所以直接調用minus(num1, num2)時,服務端收到的licenseInfo=null * @see   所以才稱為隱式聲明頭資訊 * @see 3)在SIB中對應的方法中增加licenseInfo參數,即可進行商務邏輯驗證了(本例中只是將頭資訊licenseInfo列印輸出) * @see   如果SEI中的header=true沒有指明的話,那麼SIB的方法中是無法擷取頭資訊licenseInfo值的,即便用戶端傳了 * @see ---------------------------------------------------------------------------------------------------------- * @create May 20, 2013 12:46:26 AM * @author 玄玉<http://blog.csdn.net/jadyer> */@WebService(serviceName="CalculatorServiceImpl",wsdlLocation="META-INF/wsdl/myCalculator.wsdl",endpointInterface="net.csdn.blog.jadyer.CalculatorService",targetNamespace="http://blog.csdn.net/jadyer")public class CalculatorServiceImpl implements CalculatorService {@Overridepublic int add(int a, int b) {System.out.println("[" + a + "]+[" + b + "]=" + (a+b));return a+b;}@Overridepublic int minus(int num1, int num2, String licenseInfo) {System.out.println("[" + num1 + "]-[" + num2 + "]=" + (num1-num2) + ",licenseInfo=[" + licenseInfo + "]");return num1-num2;}}

最後是用於發布WebService服務的ServerApp.java

 

 

package com.jadyer.server;import javax.xml.ws.Endpoint;import net.csdn.blog.jadyer.CalculatorServiceImpl;/** * 契約優先開發及隱式聲明頭資訊 * @see ----------------------------------------------------------------------------------------------------- * @see 開發流程 * @see 1)建立\\src\\META-INF\\wsdl\\myCalculator.wsdl檔案,並編寫其內容 * @see   File--New--Other--MyEclipse--Web Services--WSDL--輸入檔案名稱後Next * @see   這一步保持Protocol為預設的SOAP不變,SOAP Binding Options為預設document literal不變即可 * @see 2)根據wsdl檔案產生服務端代碼(wsimport -d d:/Download/ -keep -verbose myCalculator.wsdl) * @see   它會產生很多的代碼,而作為服務端的我們,只需保留一個介面類(SEI)即可............如果是用戶端,就不能刪了 * @see   若SEI報告ObjectFactory cannot be resolved to a type,則刪除@XmlSeeAlso({ObjectFactory.class}) * @see 3)編寫實作類別 * @see   在實作類別上指定@WebService(serviceName="", wsdlLocation="", endpointInterface="", targetNamespace="") * @see 4)發布服務 * @see   發布時的address可任意指定,不要求一定要與myCalculator.wsdl中的<soap:address location=""/>相同 * @see   但發布後在瀏覽器中查看wsdl時會發現,其<soap:address location=""/>值始終與發布時指定的address相同 * @see ----------------------------------------------------------------------------------------------------- * @create May 17, 2013 11:33:09 AM * @author 玄玉<http://blog.csdn.net/jadyer> */public class ServerApp {public static void main(String[] args) {Endpoint.publish("http://127.0.0.1:8088/calculatorQuery", new CalculatorServiceImpl());}}

OK,服務端程式碼範例完畢,下面是用戶端代碼

 

用戶端只有一個用於示範調用服務端的ClientApp.java

註:具體用戶端代碼由wsimport產生,詳見http://blog.csdn.net/jadyer/article/details/8692108

 

package com.jadyer.client;import java.io.IOException;import java.net.MalformedURLException;import java.net.URL;import javax.xml.namespace.QName;import javax.xml.soap.MessageFactory;import javax.xml.soap.SOAPBody;import javax.xml.soap.SOAPBodyElement;import javax.xml.soap.SOAPEnvelope;import javax.xml.soap.SOAPException;import javax.xml.soap.SOAPHeader;import javax.xml.soap.SOAPMessage;import javax.xml.ws.Dispatch;import javax.xml.ws.Service;import net.csdn.blog.jadyer.CalculatorService;import net.csdn.blog.jadyer.CalculatorServiceImpl;public class ClientApp {//服務端提供服務的連接埠是8088,如果使用Eclipse提供的TCP/IP Monitor,則此處需將8088改為TCP/IP Monitor監聽的本地連接埠private static final String wsdlLocation = "http://127.0.0.1:8088/calculatorQuery?wsdl";//取自wsdl檔案中定義的<wsdl:definitions targetNamespace=""/>的值private static final String nameSpace = "http://blog.csdn.net/jadyer";//取自wsdl檔案中定義的<wsdl:service name="">的值private static final String serviceName = "CalculatorServiceImpl";//取自wsdl檔案中定義的<wsdl:port name="">的值private static final String portName = "CalculatorServiceImplPort";/** * 隱式聲明頭資訊(使用SAAJ) * @see 本例中,服務端提供的minus()在訪問時需要提供頭資訊 * @see 如果沒提供的話,也可正常訪問並接收應答,因為本例中服務端並沒有強制驗證頭資訊(只是輸出,頂多輸出個null) * @see 通常有三種方式可以讓用戶端訪問服務端時附帶頭資訊,分別為Handler,SAAJ,代理類 */private static void soapInvoke() throws SOAPException, IOException{SOAPMessage message = MessageFactory.newInstance().createMessage();SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();SOAPBody body = envelope.getBody();SOAPHeader header = envelope.getHeader();if(null == header){header = envelope.getHeader();}//添加頭資訊header.addHeaderElement(new QName(nameSpace, "licenseInfo", "ns")).setValue("theClientLicenseInfo");//添加體資訊SOAPBodyElement sbe = body.addBodyElement(new QName(nameSpace, "minus", "ns"));sbe.addChildElement("num1").setValue("4");sbe.addChildElement("num2").setValue("1");System.out.println("invoke begin......");message.writeTo(System.out);System.out.println("");Service service = Service.create(new URL(wsdlLocation), new QName(nameSpace, serviceName));Dispatch<SOAPMessage> dispatch = service.createDispatch(new QName(nameSpace, portName), SOAPMessage.class, Service.Mode.MESSAGE);SOAPMessage respMsg = dispatch.invoke(message);respMsg.writeTo(System.out);System.out.println("\ninvoke end......");}private static void wsimportInvoke() throws MalformedURLException{CalculatorServiceImpl csl = new CalculatorServiceImpl(new URL(wsdlLocation), new QName(nameSpace, serviceName));CalculatorService cs = csl.getCalculatorServiceImplPort();System.out.println(cs.add(2, 3));System.out.println(cs.minus(2, 1));}public static void main(String[] args) throws SOAPException, IOException {wsimportInvoke();soapInvoke();}}

控制台輸出如下

 

 

//服務端[2]+[3]=5[2]-[1]=1,licenseInfo=[null][4]-[1]=3,licenseInfo=[theClientLicenseInfo]//用戶端51invoke begin......<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header><ns:licenseInfo xmlns:ns="http://blog.csdn.net/jadyer">theClientLicenseInfo</ns:licenseInfo></SOAP-ENV:Header><SOAP-ENV:Body><ns:minus xmlns:ns="http://blog.csdn.net/jadyer"><num1>4</num1><num2>1</num2></ns:minus></SOAP-ENV:Body></SOAP-ENV:Envelope><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header/><S:Body><ns2:minusResponse xmlns:ns2="http://blog.csdn.net/jadyer"><minusResult>3</minusResult></ns2:minusResponse></S:Body></S:Envelope>invoke end......

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.