第一步:新建一個webservice接口
html
@WebService
public interface IHelloWorld {
//@WebParam給參數命名,提升可代碼可讀性。此項可選
blic String sayHi(@WebParam(name="text") String text);
}
java
經過註解@WebService申明爲webservice接口
第二步,實現WebService接口
程序員
@WebService
public class HelloWorldImpl implements IHelloWorld {
public String sayHi(String name) {
System.out.println("sayHello is called by " + name);
return "Hello " + name;
}
}
web
第三步,建立服務端spring
public class Server {
private Server(){
IHelloWorld helloWorld = new HelloWorldImpl();
//建立WebService服務工廠
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
//註冊WebService接口
factory.setServiceClass(IHelloWorld.class);
//發佈接口
factory.setAddress("http://localhost:9000/HelloWorld");
factory.setServiceBean(helloWorld);
//建立WebService
factory.create();
};
public static void main(String[] args) throws InterruptedException{
//啓動服務端
new Server();
System.out.println("Server ready");
//休眠一分鐘,便於測試
Thread.sleep(1000*60);
System.out.println("Server exit");
System.exit(0);
}
}
數據庫
第四步,建立客戶端
apache
public class Client {
private Client(){};
public static void main(String[] args){
//建立WebService客戶端代理工廠
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
//註冊WebService接口
factory.setServiceClass(HelloWorld.class);
//設置WebService地址
factory.setAddress("http://localhost:9000/HelloWorld");
IHelloWorld iHelloWorld = (IHelloWorld)factory.create();
System.out.println("invoke webservice");
System.out.println("message context is:"+iHelloWorld.sayHi("
Josen"));
System.exit(0);
}
}
編程
首先,運行服務端程序
其次,打開瀏覽器,在地址欄中輸入http://localhost:9000/HelloWorld?wsdl(由於cxf自帶了一個jetty服務器),查看接口是否發佈成功,如裏瀏覽器頁面顯示下面內容,證實接口發佈成功
本文所引用的資源主要包括兩類,一類是Web服務的技術資源網站,包含了大量Web服務的技術信息,另外一類是Web服務「stack"系列技術規範,他們是一個總體的技術體系,包括UDDI、SOAP、WSDL、XML等。本文的最後給出了這些資源的連接,有興趣的讀者能夠經過這些資源連接找到所需的內容。
其中,綠色部分是先前已經定義好的而且普遍使用的傳輸層和網絡層的標準:IP、HTTP、SMTP等。而藍色部分是目前開發的Web服務的相關標準協議,包括服務調用協議SOAP、服務描述協議WSDL和服務發現/集成協議UDDI,以及服務工做流描述語言WSFL。而橙色部分描述的是更高層的待開發的關於路由、可靠性以及事務等方面的協議。***部分是各個協議層的公用機制,這些機制通常由外部的正交機制來完成。
其中,一個可使用的Web服務應當按照須要選用若干層次的功能,而無需全部的特性。可是不管如何爲了實現一個通常意義上的Web服務,具有Web服務的基礎特性:跨平臺調用和接口可機器識別,那麼必需使用WSDL和SOAP。SOAP是用來最終完成Web服務調用的,而WSDL則是用於描述如何使用 SOAP來調用Web服務的。
WSDL 是一種XML Application,他將Web服務描述定義爲一組服務訪問點,客戶端能夠經過這些服務訪問點對包含面向文檔信息或面向過程調用的服務進行訪問(相似遠程過程調用)。WSDL首先對訪問的操做和訪問時使用的請求/響應消息進行抽象描述,而後將其綁定到具體的傳輸協議和消息格式上以最終定義具體部署的服務訪問點。相關的具體部署的服務訪問點經過組合就成爲抽象的Web服務。
在具體使用中,咱們能夠對 WSDL 進行擴展(相似SOAP的可擴展性),這樣不管通訊時使用何種消息格式或網絡協議,均可以對服務訪問點及其使用的消息格式進行描述。在WSDL的框架中,可使用任意的消息格式和網絡協議,如同SOAP中可使用任意的網絡協議同樣。在WSDL規範中,定義瞭如何使用SOAP消息格式、HTTP GET/POST消息格式以及MIME格式來完成Web服務交互的規範。
WSDL 文檔將Web服務定義爲服務訪問點或端口的集合。在 WSDL 中,因爲服務訪問點和消息的抽象定義已從具體的服務部署或數據格式綁定中分離出來,所以能夠對抽象定義進行再次使用:消息,指對交換數據的抽象描述;而端口類型,指操做的抽象集合。用於特定端口類型的具體協議和數據格式規範構成了能夠再次使用的綁定。將Web訪問地址與可再次使用的綁定相關聯,能夠定義一個端口,而端口的集合則定義爲服務。所以,WSDL 文檔在Web服務的定義中使用下列元素:
api
* Types - 數據類型定義的容器,它使用某種類型系統(通常地使用XML Schema中的類型系統)。
* Message - 通訊消息的數據結構的抽象類型化定義。使用Types所定義的類型來定義整個消息的數據結構。
* Operation - 對服務中所支持的操做的抽象描述,通常單個Operation描述了一個訪問入口的請求/響應消息對。
* PortType - 對於某個訪問入口點類型所支持的操做的抽象集合,這些操做能夠由一個或多個服務訪問點來支持。
* Binding - 特定端口類型的具體協議和數據格式規範的綁定。
* Port - 定義爲協議/數據格式綁定與具體Web訪問地址組合的單個服務訪問點。
* Service - 相關服務訪問點的集合。 瀏覽器
你們能夠參考下圖,來理解一下WSDL文檔的結構組織:
其中,Types是一個數據類型定義的容器,包含了全部在消息定義中須要的XML元素的類型定義,我將在從此的文章中結合XML Schema來詳細說明如何進行類型定義。
Message具體定義了在通訊中使用的消息的數據結構,Message元素包含了一組Part元素,每一個Part元素都是最終消息的一個組成部分,每一個 Part都會引用一個DataType來表示它的結構。Part元素不支持嵌套(可使用DataType來完成這方面的須要),都是並列出現。
PortType具體定義了一種服務訪問入口的類型,何謂訪問入口的類型呢?就是傳入/傳出消息的模式及其格式。一個PortType能夠包含若干個 Operation,而一個Operation則是指訪問入口支持的一種類型的調用。在WSDL裏面支持四種訪問入口調用的模式:
1. 單請求;
2. 單響應;
3. 請求/響應;
4. 響應/請求。
在這裏請求指的是從客戶端到Web服務端,而響應指的是從Web服務端到客戶端。PortType的定義中會引用消息定義部分的一個到兩個消息,做爲請求或響應消息的格式。好比,一個股票查詢的訪問入口可能就會支持兩種請求消息,一種請求消息中指明股票代碼,而另外一種請求消息中則會指明股票的名稱,響應消息可能都是股票的價格等等。
以上三種結構描述了調用Web服務的抽象定義,這三部分與具體Web服務部署細節無關,是可複用的描述(每一個層次均可以複用)。若是與通常的對象語言作比較的話,這部分能夠堪稱是IDL描述的對象,描述了對象的接口標準,可是到底對象是用哪一種語言實現,聽從哪一種平臺的細節規範,被部署在哪臺機器上則是後面的元素所描述的。
Service描述的是一個具體的被部署的Web服務所提供的全部訪問入口的部署細節,一個Service每每會包含多個服務訪問入口,而每一個訪問入口都會使用一個Port元素來描述。
Port描述的是一個服務訪問入口的部署細節,包括經過哪一個Web地址(URL)來訪問,應當使用怎樣的消息調用模式來訪問等。其中消息調用模式則是使用Binding結構來表示。
Binding結構定義了某個PortType與某一種具體的網絡傳輸協議或消息傳輸協議相綁定,從這一層次開始,描述的內容就與具體服務的部署相關了。好比能夠將PortType與SOAP/HTTP綁定,也能夠將PortType與MIME/SMTP相綁定等。
在介紹了WSDL的主要元素以後,你們會發現,WSDL的設計理念徹底繼承了以XML爲基礎的當代Web技術標準的一向設計理念:開放。WSDL容許經過擴展使用其餘的類型定義語言(不光是XML Schema),容許使用多種網絡傳輸協議和消息格式(不光是在規範中定義的這些:SOAP/HTTP,HTTP-GET/POST以及MIME等)。同時WSDL也應用了當代軟件工程中的複用理念,分離了抽象定義層和具體部署層,使得抽象定義層的複用性大大增長。好比咱們能夠先使用抽象定義層爲一類 Web服務進行抽象定義(好比UDDI Registry,抽象定義確定是徹底一致的遵循了UDDI規範),而不一樣的運營公司能夠採用不一樣的具體部署層的描述結合抽象定義完成其自身的Web服務的描述。
WSDL文檔示例
下例是一個提供股票報價的簡單Web服務的 WSDL 定義。該服務支持名爲 GetLastTradePrice 的單一操做,這個操做是經過在 HTTP 上運行 SOAP 1.1 協議來實現的。該請求接受一個類型爲字符串的 tickerSymbol,並返回類型爲浮點數的價格。
<?xml version="1.0"?>
<definitions name="StockQuote"
targetNamespace="http://example.com/stockquote.wsdl"
xmlns:tns="http://example.com/stockquote.wsdl"
xmlns:xsd1="http://example.com/stockquote.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<schema targetNamespace="http://example.com/stockquote.xsd"
xmlns="http://www.w3.org/1999/XMLSchema">
<element name="TradePriceRequest">
<complexType>
<all>
<element name="tickerSymbol" type="string"/>
</all>
</complexType>
</element>
<element name="TradePriceResult">
<complexType>
<all>
<element name="price" type="float"/>
</all>
</complexType>
</element>
</schema>
</types>
上面這部分是數據類型的定義,其中爲定義了兩個元素的結構:
* TradePriceRequest(交易價格請求): 將該元素定義爲包含一個字符串元素(tickerSymbol)的複合類型元素。
* TradePriceResult(交易價格): 將該元素定義爲一個包含一個浮點數元素(price)的複合類型元素。
message name="GetLastTradePriceInput">
<part name="body" element="xsd1:TradePriceRequest"/>
</message>
<message name="GetLastTradePriceOutput">
<part name="body" element="xsd1:TradePriceResult"/>
</message>
這部分是消息格式的抽象定義,其中定義了兩個消息格式:
* GetlastTradePriceInput(獲取最後交易價格的請求消息格式): 由一個消息片段組成,該消息片段的名字是body,包含的具體元素類型是TradePriceRequest。(前面已經定義過了)
* GetLastTradePriceOutput(獲取最後交易價格的響應消息格式) : 由一個消息片段組成,該消息片段的名字是body,包含的具體元素類型是TradePriceResult。(前面已經定義過了)
<portType name="StockQuotePortType">
<operation name="GetLastTradePrice">
<input message="tns:GetLastTradePriceInput"/>
<output message="tns:GetLastTradePriceOutput"/>
</operation>
</portType>
這部分定義了服務訪問點的調用模式的類型,代表StockQuoteService的某個入口類型是請求/響應模式,請求消息是GetlastTradePriceInput,而響應消息是GetLastTradePriceOutput。
binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="GetLastTradePrice">
<soap:operation soapAction="http://example.com/GetLastTradePrice"/>
<input>
<soap:body use="literal" namespace="http://example.com/stockquote.xsd"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="literal" namespace="http://example.com/stockquote.xsd"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</soap:operation>
</operation>
</soap:binding>
</binding>
這部分將服務訪問點的抽象定義與SOAP HTTP綁定,描述如何經過SOAP/HTTP來訪問按照前面描述的訪問入口點類型部署的訪問入口。其中規定了在具體SOAP調用時,應當使用的 soapAction是"http://example.com/GetLastTradePrice",而請求/響應消息的編碼風格都應當採用SOAP 規範默認定義的編碼風格" http://schemas.xmlsoap.org/soap/encoding/"。
<service name="StockQuoteService">
<documentation>股票查詢服務</documentation>
<port name="StockQuotePort" binding="tns:StockQuoteBinding">
<soap:address location="http://example.com/stockquote"/>
</port>
</service>
</definitions>
這部分是具體的Web服務的定義,在這個名爲StockQuoteService的Web服務中,提供了一個服務訪問入口,訪問地址是"http://example.com/stockquote",使用的消息模式是由前面的binding所定義的。
按照這個WSDL文檔的描述,在具體Web服務的使用中,具體發生的SOAP交互可能以下面所示:
SOAP消息請求:
POST /StockQuote HTTP/1.1
Host: example.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "http://example.com/GetLastTradePrice"
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:TradePriceRequest xmlns:m="http://example.com/stockquote.xsd">
<tickerSymbol>MSFT</tickerSymbol >
</m:TradePriceRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP消息響應:
HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<SOAP-ENV:Body>
<m:TradePriceResult xmlns:m=" http://example.com/stockquote.xsd ">
<price>74.5</price>
</m:TradePriceResult >
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
JAX-WS規範是一組XML web services的JAVA API。JAX-WS容許開發者能夠選擇RPC-oriented或者message-oriented 來實現本身的web services。
在 JAX-WS中,一個遠程調用能夠轉換爲一個基於XML的協議例如SOAP。在使用JAX-WS過程當中,開發者不須要編寫任何生成和處理SOAP消息的代碼。JAX-WS的運行時實現會將這些API的調用轉換成爲對於SOAP消息。
在服務器端,用戶只須要經過Java語言定義遠程調用所須要實現的接口SEI (service endpoint interface),並提供相關的實現,經過調用JAX-WS的服務發佈接口就能夠將其發佈爲WebService接口。
在客戶端,用戶能夠經過JAX-WS的API建立一個代理(用本地對象來替代遠程的服務)來實現對於遠程服務器端的調用。
經過web service所提供的互操做環境,咱們能夠用JAX-WS輕鬆實現JAVA平臺與其餘編程環境(.net等)的互操做。
JAX-WS工做原理以下圖所示
:
前面幾節都是講一些理論知識,如今又用一個例子來講明一下,這一節咱們就CXF框架對象傳遞進行講解。
@XmlRootElement(name="Customer")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = {"name","age"})
public class Customer {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@XmlRootElement-指定XML根元素名稱(可選)
@XmlAccessorType-控制屬性或方法序列化
FIELD-對每一個非靜態,非瞬變屬性JAXB工具自動綁定成XML,除非註明XmlTransient
NONE-不作任何處理
PROPERTY-對具備set/get方法的屬性進行綁定,除非註明XmlTransient
PUBLIC_MEMBER -對有set/get方法的屬性或具備共公訪問權限的屬性進行綁定,除非注
明XmlTransient
@XmlType-映射一個類或一個枚舉類型成一個XML Schema類型
@XmlType-映射一個類或一個枚舉類型成一個XML Schema類型
第二步:建立WebService接口
@WebService
public interface HelloService {
public void save(Customer c1,Customer c2);
public void test(String args);
public Customer get(int id);
}
@WebService
public class HelloServiceImpl implements HelloService {
public void save(Customer c1, Customer c2) {
System.out.println(c1.getAge()+"---"+c2.getAge());
System.out.println(c1.getName()+"---"+c2.getName());
}
public void test(String args) {
System.out.println(args);
}
public Customer get(int id) {
Customer cus = new Customer();
cus.setAge(100);
cus.setName("Josen");
return cus;
}
}
第四步:建立服務端
public class SoapServer {
public static void main(String[] args){
//兩種方法,任選一種發佈WebService接口
//Endpoint.publish("http://localhost:8080/helloService", new
HelloServiceImpl());
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
factory.setAddress("http://localhost:8080/helloService");
factory.setServiceClass(HelloServiceImpl.class);
factory.getInInterceptors().add(new LoggingInInterceptor());
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.create();
}
第五步:建立客戶端
public class SoapClient {
public static void main(String[] args){
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setAddress("http://localhost:8080/helloService");
factory.setServiceClass(HelloService.class);
factory.setServiceClass(HelloServiceImpl.class);
factory.getInInterceptors().add(new LoggingInInterceptor());
HelloService service = (HelloService)factory.create();
Customer c1 = new Customer();
c1.setAge(1);
c1.setName("aaa");
Customer c2 = new Customer();
c2.setAge(2);
c2.setName("bbb");
service.save(c1, c2);
service.test("aaaaaaaaaaaaa");
}
}
最後,測試程序
運行服務端程序,在瀏覽器地址欄輸入http://localhost:8080/helloService?wsdl查看接口是否發佈成功。成功則運行一下客戶端程序,看看對象傳輸是否成功。
如今咱們來分析一下控制打印的日誌信息。
信息: Inbound Message
----------------------------
ID: 1
Address: /HelloWorld
Encoding: UTF-8
Content-Type: text/xml; charset=UTF-8
Headers: {content-type=[text/xml; charset=UTF-8], connection=[keep-alive], Host=[localhost:9000], Content-Length=[184], SOAPAction=[""], User-Agent=[Apache CXF 2.2.2], Content-Type=[text/xml; charset=UTF-8], Accept=[*/*], Pragma=[no-cache], Cache-Control=[no-cache]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns1:say xmlns:ns1="http://client.itdcl.com/"><text> Josen</text></ns1:say></soap:Body></soap:Envelope>
--------------------------------------
2010-1-9 20:41:56 org.apache.cxf.interceptor.LoggingOutInterceptor$LoggingCallback onClose
信息: Outbound Message
---------------------------
ID: 1
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><text xmlns="http://client.itdcl.com/">hi Josen</text></soap:Header><soap:Body><ns1:sayResponse xmlns:ns1="http://client.itdcl.com/"></ns1:sayResponse></soap:Body></soap:Envelope>
--------------------------------------
2010-01-09 20:41:56.578::INFO: seeing JVM BUG(s) - cancelling interestOps==0
當客戶端向服器發送請求時,服務端LoggingInInterceptor拉截客戶端發送過來的SOAP消息,以下:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns1:sayHi xmlns:ns1="http://client.itdcl.com/">
<text>Josen</text>
</ns1:sayHi>
</soap:Body>
</soap:Envelope>
客戶端將請求信息封閉在<soap:Body></soap:Body>中,固然也能夠將其放到<soap:Header></soap:Header>,只要在@WebParam中的header設置成true,默認爲false;
服務器接到請求以後,響應客戶端。一樣以SOAP形式將信息封裝好發回客戶端,SOAP信息以下:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<text xmlns="http://client.itdcl.com/">hi Josen</text>
</soap:Header>
<soap:Body>
<ns1:sayResponse xmlns:ns1="http://client.itdcl.com/"></ns1:sayResponse>
</soap:Body>
</soap:Envelope>
前面幾節咱們講解對象傳遞,可是一般狀況下咱們不直接傳對象,由於直接傳遞對象安全性差,並且暴露了實體對象。因此咱們選擇傳遞XML文件,固然也能夠傳遞JSON對象。這節我只針對傳遞XML,那麼JAVA綁定成XML,服務端將XML解析成Java對象有什麼工具可用嗎,其實這樣的工具多的是。這裏我選擇一個比較簡單的JAXB工具來說解一下。
JAXB(Java Architecture for XML Binding)提供了一個快速而方便的方式綁定XML Schemas和java,使java程序員可以很方便的在java應用程序中處理XML數據。JAXB提供了將XML文檔解組爲java內容樹的方法,以及將java內容樹從新編組回XML文檔的方法。JAXB一樣也提供了一種從java對象生成XML Schema的方式。
裏有幾個重要的定義:
編組(Marshalling)是把內存中的數據轉化到存儲媒介上的過程。所以在 Java 和 XML 環境中,編組就是把一些 Java 對象轉化成一個(或多個) XML 文檔。在數據庫環境中,則是把 Java 表示的數據存入數據庫。顯然,編組的祕密在於把 Java 實例中的面向對象結構轉化成適用於 XML 的 扁平結構,或者 RDBMS 中的關係結構(使用 Java 技術轉換到 OODBMS 實際上很簡單)。工做原理以下圖所示
解組(Unmarshalling) 是把數據從存儲媒介轉換到內存中的過程--正好與編組相反。所以須要把 XML 文檔解組到 Java VM 中。這裏的複雜性不是在扁平數據中,由於這不是必需的,而在於從正確的數據到正確的 Java 代碼變量的映射。若是映射是錯誤的,就不可能正確地訪問數據。固然,若是再嘗試從新編組還會形成更大的問題,而且問題傳播得很快。工做原理以下圖所示:
往返(Round-tripping)多是最重要也最容易誤解的數據綁定術語。往返用於描述從存儲媒介到內存而後回到存儲媒介的完整循 環。在 XML 和 Java 技術環境中,這就意味着從 XML 文檔到 Java 實例變量,而後再回到 XML 文檔。正確的往返要求,若是中間沒有修改數據,XML 輸入和 XML 輸出應該是等同的。
咱們還以例子來講明它的工做原理,直觀點。
第一步,建立一個Customer對象
@XmlRootElement(name="customer")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
public class Customer {
@XmlAttribute(required = true)
protected String name;
@XmlAttribute(required = true)
protected int age;
/**
* Gets the value of the name property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getName() {
return name;
}
/**
* Sets the value of the name property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setName(String value) {
this.name = value;
}
/**
* Gets the value of the age property.
*
*/
public int getAge() {
return age;
}
/**
* Sets the value of the age property.
*
*/
public void setAge(int value) {
this.age = value;
}
}
第二步,建立一個測試類
public class SoapClient {
private final static String MODEL = "com.itdcl.model";
public static void main(String[] args) throws ParserConfigurationException, JAXBException, TransformerException{
ObjectFactory factory = new ObjectFactory();
Customer customer = factory.createCustomer();
customer.setAge(20);
customer.setName("Josen");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
JAXBContext jaxbContext = JAXBContext.newInstance(MODEL);
//Java對象轉換成XML
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(customer, doc);
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
String xmlString = writer.toString();
System.out.println(xmlString);
//XML轉換成Java對象
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
Customer cus = (Customer)unmarshaller.unmarshal(reader);
System.out.println("Age:"+cus.getAge());
System.out.println("Name:"+cus.getName());
}
}
第三步,運行一個測試類,看看效果如何。Java與XML之間轉換如此簡單
編組操做:利用上面生成的java文件執行編組操做。
JAXBContext jaxbContext = JAXBContext.newInstance(MODEL);
//Java對象轉換成XML
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(customer, doc);
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
String xmlString = writer.toString();
System.out.println(xmlString);
解組操做:經過xml文件執行解組操做。
JAXBContext jaxbContext = JAXBContext.newInstance(MODEL);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
Customer cus = (Customer)unmarshaller.unmarshal(reader);
System.out.println("Age:"+cus.getAge());
System.out.println("Name:"+cus.getName());
整合Spring框架
首先,在前面基礎上再導入幾個spring要用到的幾個.jar包:
spring-core.jar
spring-jdbc.jar
spring-context.jar
spring-orm.jar
spring-beans.jar
spring-tx.jar
包導入完以後,咱們還不能開如干活,配置幾薦參數,便於下一步工做。
配置CXF框架
個人電腦->屬性->高級->環境變量
建立一個CXF_HOEM變量,值爲CXF框架所在根目錄,修改一下
CLASSPATH=%CXF_HOME%/lib;PATH=%CXF_HOME%/bin;這時有會問爲何要配置這兩個參數據呢,其實配置這兩個參數用途與配置JAVA變量一下,在DOS窗口下直接運行java2ws,wsdl2java等可執行文件。固然你沒有配置也能夠進到CXF框架的bin目錄下遠行這個幾個可執行文件。
配置好了後,你在DOS窗口下輸入java2ws,看看配置是否效。確定沒有成功,緣由是使用6.0的JDK,咱們還得在%JAVA_HOME%/jre/lib目錄下建立一下endorsed文件夾,將jaxb-api.jar,jaxws.jar拷貝進去。如今再運一下java2ws,成功運行,配置生效了。
基本工做作得差很少,整合spring框架正式開始:
第一步:新一個web project,導入要用到.jar包,其實CXF利用org.apache.cxf.transport.servlet.CXFServlet來攔截全部web請求,將其配置到web.xml中。配置以下:
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext-*.xml,/WEB-INF/classes/webservice.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
[color=red]<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>[/color]
</web-app>
注意一下綠色字體
CXF框架配置好了,我就來開發一個WebService接口
@WebService
public interface IService {
//public void save(@WebParam(name="info")String xml);
public void save(@WebParam(name="dto")UserInfoDTO dto,@WebParam(name="flag")boolean flag);
public void update(@WebParam(name="info")String xml);
public void delete(@WebParam(name="id")int id);
public @WebResult(name="String")String get(@WebParam(name="id")int id);
}
這裏面有四個方法,其中有一個涉及到對象,這一點前面一節講到怎麼處理它。放在這裏是再回顧前節內容。
建立一個WebService接口實現類
@WebService
public class ServiceImpl implements IService {
private Logger log = LoggerFactory.getLogger(ServiceImpl.class);
public void delete(int id) {
log.info("delete id is {} user"+id);
}
public void save(UserInfoDTO dto,boolean flag) {
System.out.println("name:"+dto.getName());
}
public void update(String xml) {
}
public String get(int id){
return null;
}
}
因爲本節只講解與Spring整合,沒有涉及到數據庫,因就打印一下傳遞過來的對象內容來證實整合成功。與spring,hibernate整合後面章節會講到,請留意後面章節。
傳遞對象,固然建立對象啦
@XmlType(name="ServerUserInfo")
@XmlAccessorType(XmlAccessType.FIELD)
public class UserInfoDTO implements java.io.Serializable {
private static final long serialVersionUID = -4666026219400887433L;
private Integer id;
private String name;
private Integer age;
private Integer address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getAddress() {
return address;
}
public void setAddress(Integer address) {
this.address = address;
}
public UserInfoDTO() {
}
}
作了這麼多工做,有人又會問,怎麼如今作的與spring框架就沒一點聯繫,呵呵,確實是這樣。開戲開場了,利用Spring來發布WebService接口:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
[color=red]xmlns:jaxws="http://cxf.apache.org/jaxws"[/color]
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
[color=red]
<!--導入與CXF框架有關的xml-->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />[/color]
<!--布布WebService接口-->
<jaxws:endpoint id="service"
implementor="com.itdcl.service.ServiceImpl" address="/Service">
</jaxws:endpoint>
</beans>
服務端開發工做基本結束。如今打包部署到Tomcat6.0.18(本人目前使用的測試服務器,你固然可使用別的服務器)
服務器啓動完成後,打開瀏覽器在地址欄中輸入http://localhost:port/project name/Service?wsdl看看接口是否發成功。
接下來就是開發一個客戶端了。
另建一個web project,如今前面的參數配置要起做用了,cmd進入DOS環境下輸入wsdl2java -p com.itdcl.service(自定義包名) http://localhost:port/project name/Service?wsdl(前提是服務器沒中止)
敲擊Enter,wsdl2java工具幫你將發佈出來的wsdl文件轉換成webservice接口,到指定目錄下將com.itdcl.service拷到剛纔另建的web project的src目錄下。
如今,咱們就來建立一個客戶端程序:
public class SampleClient {
ublic static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"beans.xml");
IService service = (IService)context.getBean("service");
ServerUserInfo userInfo = new ServerUserInfo();
userInfo.setAddress(1);
userInfo.setAge(100);
userInfo.setName("Jason");
service.save(userInfo,true);
service.delete(1);
}
看得仔細的朋友就會問了,ServerUserInfo 這個類那來的,你進到com.itdcl.service目錄下就會看到,是WebService發佈出來的。
到目前爲止,客戶端尚未完成,你沒有看到程序中ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");這個bean.xml就是spring框架來注刪除WebService接口的。好啦,建立一個bean.xml,內容以下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
[color=red]xmlns:jaxws="http://cxf.apache.org/jaxws[/color]"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
[color=red]http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd[/color]">
<jaxws:client id="service"
address="http://localhost:9999/cxf/Service"
serviceClass="com.itdcl.service.IService" />
</beans>
這一節咱們來探討一下WebService安全問題,若是全部系統都運行在一個封閉的局域網內,那麼能夠不考慮網絡***,拒絕服務,消息篡改,竊取等問題。但一般狀況都接入互聯網,那麼我就得考慮信息安全問題,像前面那樣直接將消息裸傳,確定不行。那麼,咱們就得給消息加密。CXF能夠結合WSS4J來對消息安全進行管理,可使用令牌,X.509認證對消息頭或內容進行加密。這節我只對令牌加密作一個簡單的描述,咱們還以Demo的形式來說解一下。
這個Demo是在CXF+Spring+Hibernate的基礎修改而成。在這裏我只針對修改的東西進行講解。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<jaxws:endpoint id="service"
implementor="com.itdcl.service.ServiceImpl" address="/Service">
<jaxws:inInterceptors>
<bean
class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean
class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor" />
<bean
class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType"
value="PasswordText" />
<entry key="user" value="cxfServer" />
<entry key="passwordCallbackRef">
<ref bean="serverPasswordCallback" />
</entry>
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
</jaxws:endpoint>
<bean id="serverPasswordCallback"
class="com.itdcl.ws.ServerPasswordCallback" />
</beans>
action:UsernameToken指使用用戶令牌
passwordType:PasswordText指密碼加密策略,這裏直接文本
user:cxfServer指別名
passwordCallBackRef:serverPasswordCallback指消息驗證
package com.itdcl.ws;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ServerPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
String pw = pc.getPassword();
String idf = pc.getIdentifier();
System.out.println("password:"+pw);
System.out.println("identifier:"+idf);
if (pw.equals("josen") && idf.equals("admin")) {
// 驗證經過
} else {
throw new SecurityException("驗證失敗");
}
}
}
消息驗證類經過實現CallbackHandler接口,實現handle方法來進行用戶認證。
那麼,客戶端又怎樣來驗證消息是否確呢。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<jaxws:client id="service"
address="http://localhost:9999/cxf/Service"
serviceClass="com.itdcl.service.IService">
<jaxws:outInterceptors>
<bean
class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<bean
class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />
<bean
class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken" />
<entry key="passwordType"
value="PasswordText" />
<entry key="user" value="cxfClient" />
<entry key="passwordCallbackRef">
<ref bean="clientPasswordCallback" />
</entry>
</map>
</constructor-arg>
</bean>
</jaxws:outInterceptors>
</jaxws:client>
<bean id="clientPasswordCallback"
class="com.itdcl.ws.ClientPasswordCallback" />
</beans>
客戶端在發送SOAP時對消息對認證,策略跟服務端同樣。可是認證類有所區別:
package com.itdcl.ws;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ClientPasswordCallback implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for(int i=0;i<callbacks.length;i++)
{
WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
pc.setPassword("josen");
pc.setIdentifier("admin");
}
}
}
客戶端在發送消息,設置好用戶名和密碼。服務端用相應的用戶名和密碼進行驗證。 令牌驗證就如此簡單。