使用 Apache CXF 實現 Web Service 詳解

1.Web service的概念

什麼是WebService呢?從表面上看,Web Service就是一個應用程序,它向外界暴露出一個可以經過Web進行調用的API。這就是說,你可以用編程的方法經過Web調用來實現某個功能的應用程序。從深層次上看,Web Service是一種新的Web應用程序分支,它們是自包含、自描述、模塊化的應用,能夠在網絡(一般爲Web)中被描述、發佈、查找以及經過Web來調用。一旦部署之後,其餘Web Service應用程序能夠發現並調用它部署的服務。Web Service是基於網絡的、分佈式的模塊化組件,它執行特定的任務,遵照具體的技術規範,這些規範使得Web Service能與其餘兼容的組件進行互操做。它可使用標準的互聯網協議,像超文本傳輸協議HTTP和XML,將功能體如今互聯網和企業內部網上。Web Service平臺是一套標準,它定義了應用程序如何在Web上實現互操做性。你能夠用你喜歡的任何語言,在你喜歡的任何平臺上寫Web Service。Web Service是構建互聯網分佈式系統的基本部件。"網絡服務"(WebService)的本質,就是經過網絡調用其餘網站的資源。 php

舉例來講,寫一個「四川大地震圖片牆」,它能動態顯示關於四川地震的最新圖片。可是,全部的圖片都不是儲存在本身的服務器上,而是來自flickr.com。只須要發出一個動態請求,要求flickr.com向本身提供圖片。這種狀況下,flickr.com提供的就是一種Web service。若是把圖片都存放在本地服務器,不調用flickr.com,那麼我就是在使用「本地服務」。 html

因此,Webservice讓你的網站可使用其餘網站的資源,好比在網頁上顯示天氣、地圖、twitter上的最新動態等等。 java

2.Web Service架構和雲

若是一個軟件的主要部分採用了「網絡服務」,即它把存儲或計算環節「外包」給其餘網站了,那麼咱們就說這個軟件屬於Web Service架構。 程序員

Web Service架構的基本思想,就是儘可能把非核心功能交給其餘人去作,本身全力開發核心功能。好比,若是你要開發一個相冊軟件,徹底可使用Flickr的網絡服務,把相片都儲存到它上面,你只要全力作好相冊自己就能夠了。整體上看,凡是不屬於你核心競爭力的功能,都應該把它「外包」出去。 web

最近很紅的「雲計算」(cloudcomputing)或者「雲服務」(cloud services),實際上就是Web Service的同義詞,不過更形象一些罷了。它們不說你把事情交給其餘計算機去作,而說你把事情交給「雲」去作。 spring

3.Web Service的優點

Web Servcie最主要的優勢是,使用不一樣程序和在不一樣系統平臺上開發出來的程序,均可以相互通訊。SOAP做爲Web Service的基本通訊協議,簡單,投入和使用的代價也很小。Web Service使用標準的互聯網協議-XML、HTTP和TCP/IP。 apache

Web Service有如下的優越性: 編程

1)平臺無關。無論你使用什麼平臺,均可以使用Web service。 api

2)編程語言無關。只要遵照相關協議,就可使用任意編程語言,向其餘網站要求Web service。這大大增長了web service的適用性,下降了對程序員的要求。 數組

3)對於Webservice提供者來講,部署、升級和維護Web service都很是單純,不須要考慮客戶端兼容問題,並且一次性就能完成。

4)對於Webservice使用者來講,能夠輕易實現多種數據、多種服務的聚合(mashup),所以可以作出一些之前根本沒法想像的事情。

4.Web Service 三個基本技術 

Web Service經過標準通訊協議,在互聯網上發佈有用的程序模塊(以服務的方式),目前大部分是用SOAP來做通訊協議。

Web Service提供一份詳細的接口說明書,來幫助用戶構建應用程序,這個接口說明書叫做WSDL(Web Service Description Language)。

一般已發佈的WebService要註冊到管理服務器,這樣便於使用者查詢和使用。這個是經過UDDI(Universal Discovery Description and Integration)來完成的。

4.1 SOAP 

SOAP是Web Service的基本通訊協議,是一種規範,用來定義SOAP消息的XML格式(XMLFormat)。包含在一對 SOAP 元素(SOAPElements)中的、結構正確的 XML 段就是 SOAP 消息。

SOAP 規範還介紹瞭如何將程序數據表示爲 XML,以及如何使用 SOAP 進行遠程過程調用 (RPC)。

最後SOAP規範還定義了HTTP消息是怎樣傳輸SOAP消息的。這並不表明SOAP只能用HTTP來做爲傳輸協議,MSMQ、SMTP、TCP/IP均可以作SOAP的傳輸協議。

4.2 WSDL 

Web Services Description Language的縮寫,是一個用來描述Web服務和說明如何與Web服務通訊的XML語言。WSDL是Web Service的描述語言,用於描述Web Service的服務,接口綁定等,爲用戶提供詳細的接口說明書。

舉個例子,你要使用供應商的WebService構建應用程序。你能夠向供應商索取使用Web Service的範例,而後按照範例來構建應用程序。這樣可能出現意料不到的錯誤,好比說,你在程序中使用的客戶代碼的數據類型是integer,而供應商使用的數據類型是string。WSDL詳細定義客戶端消息的格式,須要什麼樣的參數,這樣能夠避免沒必要要的錯誤。

要查看 WSDL 的值,能夠假設您要調用由您的一位業務夥伴提供的 SOAP 方法。您能夠要求對方提供一些 SOAP 消息示例,而後編寫您的應用程序以生成並使用與示例相似的消息。WSDL 經過明確的表示法指定請求消息必須包含的內容以及響應消息的樣式。

WSDL 文件用於說明消息格式的表示法以 XML 架構標準爲基礎,這意味着它與編程語言無關,並且以標準爲基礎,所以適用於說明可從不一樣平臺、以不一樣編程語言訪問的 XML Web Service 接口。除說明消息內容外,WSDL 還定義了服務的位置,以及使用什麼通訊協議與服務進行通訊。WSDL 文件定義了編寫使用 XML Web Service 的程序所需的所有內容。

4.3 UDDI

UniversalDescription Discovery and Integration即統一描述、發現和集成協議。UDDI實現了一組可公開訪問的接口,經過這些接口,網絡服務能夠向服務信息庫註冊其服務信息、服務需求者能夠找到分散在世界各地的網絡服務。

UDDI 目錄條目是介紹所提供的業務和服務的 XML 文件。能夠把它比喻成電話本,電話本里記錄的是電話信息,而UDDI記錄的是Web Service信息。你能夠不把Web Service註冊到UDDI。但若是要讓全球的人知道你的Web Service,最好仍是註冊到UDDI。 

UDDI 目錄還包含若干種方法,可用於搜索構建您的應用程序所需的服務。例如,您能夠搜索特定地理位置的服務提供商或者搜索特定的業務類型。以後,UDDI 目錄將提供信息、聯繫方式、連接和技術數據,以便您肯定能知足須要的服務。

UDDI 容許您查找提供所需的Web 服務的公司。若是您已經知道要與誰進行業務合做,但尚不瞭解它還能提供哪些服務,這時該如何處理呢?WS-Inspection規範容許您瀏覽特定服務器上提供的 XML Web Service 的集合,從中查找所需的服務。

UDDI 目錄說明文件也是一個XML文檔,它包括下面三個部分:

「白頁(WhitePaper)」介紹提供Web Service的公司信息,好比名稱、地址、聯繫方式等等;

「黃頁(YellowPaper)」 說明UDDI目錄的分類,包括基於標準分類法的行業類別,好比說金融、服務和印刷等等;

「綠頁(greenPaper)」 詳細介紹了訪問服務的接口,以便用戶可以編寫應用程序以使用 Web 服務。

5.Web Service的開源實現

Web Service更可能是一種標準,而不是一種具體的技術。不一樣的平臺,不一樣的語言大都提供Web Service的開發實現,在JAVA領域,Web Service的框架不少,例如:Axis1&2,Xfire,CXF,java6自帶Web Service引擎。

1)從JavaSE6.0開始,Java引入了對Web Service的原生支持。咱們只須要簡單的使用Java的Annotation標籤便可將標準的Java方法發佈成Web Service。但不是全部的Java類均可以發佈成Web Service。Java類若要成爲一個實現了Web Service的bean,它須要遵循下邊這些原則:

Ø  這個類必須是public類

Ø  這些類不能是final的或者abstract

Ø  這個類必須有一個公共的默認構造函數

Ø  這個類絕對不能有finalize()方法

2)Axis2(Apache eXtensible Interaction System)是Apache下的一個重量級WebService框架,準確說它是一個Web Services / SOAP /WSDL 的引擎,是WebService框架的集大成者,它能不但能製做和發佈WebService,並且能夠生成Java和其餘語言版WebService客戶端和服務端代碼。這是它的優點所在。可是,這也不可避免的致使了Axis2的複雜性,使用過的開發者都知道,它所依賴的包數量和大小都是很驚人的,打包部署發佈都比較麻煩,不能很好的與現有應用整合爲一體。可是若是你要開發Java以外別的語言客戶端,Axis2提供的豐富工具將是你不二的選擇。

3)XFire是一個高性能的WebService框架,在Java6以前,它的知名度甚至超過了Apache的Axis2,XFire的優勢是開發方便,與現有的Web整合很好,能夠融爲一體,而且開發也很方便。可是對Java以外的語言,沒有提供相關的代碼工具。XFire後來被Apache收購了,緣由是它太優秀了,收購後,隨着Java6 JWS的興起,開源的WebService引擎已經再也不被看好,漸漸的都敗落了。

4)Apache CXF是Apache旗下一個重磅的SOA簡易框架,它實現了ESB(企業服務總線)。CXF 繼承了 Celtix 和 XFire 兩大開源項目的精華,不只提供了對JAX-WS 全面的支持,而且提供了多種Binding 、DataBinding、Transport 以及各類 Format 的支持,而且能夠根據實際項目的須要,採用代碼優先(Code First)或者 WSDL 優先(WSDL First)來輕鬆地實現 Web Services 的發佈和使用。並且能夠自然的和Spring進行無縫集成。Apache CXF已是一個正式的Apache頂級項目。

6.基於 Apache CXF的Web Service開發

Web Service 支持不一樣語言開發,而不關心服務端或者客戶端採用何種語言。這裏講解利用cxf進行Web Service開發。

這裏先講解Java中的web服務規範:

Java中共有三種Web Service規範,分別是JAXM&SAAJ、JAX-WS(JAX-RPC)、JAX-RS。

JAX-WS(Java API ForXML-WebService),JDK1.6自帶的版本爲JAX-WS2.1,其底層支持爲JAXB。早期的基於SOAP的JAVA的Web服務規範JAX-RPC(Java API For XML-RemoteProcedure Call)目前已經被JAX-WS規範取代,JAX-WS是JAX-RPC的演進版本,但JAX-WS並不徹底向後兼容JAX-RPC,兩者最大的區別就是RPC/encoded樣式的WSDL,JAX-WS已經不提供這種支持。

這裏的JAX-WS規範咱們採用Apache CXF做爲實現。

簡單的說下注意事項:當你使用的是JDK1.5的時候,就必需要有jaxws-api-2.0.jar這個包的支持,若是使用的是JDK1.6就不用使用這個包了。由於1.6裏已經有相關實現。

6.1 簡單的CXF應用

6.1.1 服務端開發

1)首先下載CXF開發要用到的相關包,目前最新版本是apache-cxf-2.3.1。下載地址:

http://www.apache.org/dyn/closer.cgi?path=%2Fcxf%2F2.3.1%2Fapache-cxf-2.3.1.zip

下載解壓後在apache-cxf-2.3.1目錄下lib目錄下,有全部要用到的jar包。

2)建一個web項目,將apache-cxf-2.1.3\lib目錄下全部包添加到項目中。

注:這些包裏面有jetty,cxf,spring的相關包,能夠根據須要進行添加,若是不和spring進行整合,則不須要spring相關包。Jetty是一個相似於tomcat的web服務器,內置在cxf中,用於發佈web服務。若是用jetty發佈服務,則須要添加它的包,若是用tomcat則不須要。

3)寫好一個接口和實現類(具體見demo中IHelloService和HelloServiceImpl),本demo中數據綁定方式採用cxf默認的jaxb方式,也能夠採用aegis,其優勢是不用jaxb中的註解方式。(基於SOAP的Web服務可用單個Java類的實現,可是最好是用「接口+實現」的方式來實現最佳。Web服務的接口稱爲SEI,即ServiceEndpoint Interface;而Web服務的實現稱爲SIB,即Service Implementation Bean。 SIB能夠是一個POJO,也能夠是無狀態的會話EJB。)

4)發佈服務

一種是經過CXF內置的Jetty應用服務器發佈(見方法一,二),一種是經過tomcat發佈(見方法三)。

Ø  方法一:使用SunJAX-WS 2中Endpoint.publish進行發佈。(不須要其餘配置與包

Endpoint endpoint =

Endpoint.publish("http://localhost:8080/WSCXF/helloService",

new HelloServiceImpl());//這裏是實現類

System.out.println("WS發佈成功!");

Ø  方法二:用CXF的JaxWsServerFactoryBean類進行發佈。(須要CXF相關包)

HelloServiceImpl impl = new HelloServiceImpl();

JaxWsServerFactoryBean factoryBean = newJaxWsServerFactoryBean();

factoryBean.setAddress("http://localhost:8080/WSCXF/helloService");

factoryBean.setServiceClass(IHelloService.class);//接口類

factoryBean.setServiceBean(impl);

factoryBean.create();

System.out.println("WS發佈成功!");

方法一或者方法二都是發佈到Jetty下。在main方法中運行方法一或者方法二代碼,web服務就發佈成功了。

Ø  方法三:利用cxf和spring整合在tomcat下進行發佈。具體方法在後面的spring整合cxf時講到。

Ø  方法四:重寫loadBus方法。

書寫一個類覆蓋org.apache.cxf.transport.servlet.CXFNonSpringServlet的loadBus方法指定BUS以及發佈你的web服務。

具體可查閱資料:

http://blog.csdn.net/zq9017197/archive/2010/12/26/6099684.aspx

查看web服務是否發佈成功:

訪問http://localhost:8080/WSCXF/helloService?wsdl 查看wsdl文件

6.1.2 客戶端調用

客戶端調用只須要服務端提供一個webservice的發佈地址便可。不關心服務端發佈方式等。

1)客戶端代碼生成

Ø  方法一:使用MyEclipse工具生成。

new-other-myeclipse-web service-web service client根據設置嚮導能夠生成客戶端,但最好使用CXF的wsdl2java來完成,由於CXF2.2+版本開始支持JAX-WS2.1規範,而MyEclipse自帶的是xfire的一個插件,生成的客戶端代碼可能不是最新規範的。

Ø  方法二:經過wsdl2java的命令生成客戶端代碼。

先進入dos窗口,進入apache-cxf-2.3.1\bin所在目錄,輸入命令:

wsdl2java -pcom.jaxb.client -d e:/ http://127.0.0.1:8080/WSCXF/helloService?wsdl

命令格式爲:wsdl2java –p 包名 –d 生成代碼存放目錄wsdl的url

其中的wsdl的url爲要調用的webservice的服務地址

附加:wsdl2java用法:

wsdl2java -p com-d src -all  aa.wsdl

-p 指定其wsdl的命名空間,也就是要生成代碼的包名;

-d 指定要產生代碼所在目錄;

-client 生成客戶端測試web service的代碼;

-server 生成服務器啓動web service的代碼;

-impl 生成web service的實現代碼;

-ant  生成build.xml文件;

-all 生成全部開始端點代碼:types,serviceproxy, service interface, server mainline, client mainline, implementation object,and an Ant build.xml file。

詳細用法見:http://cwiki.apache.org/CXF20DOC/wsdl-to-java.html

2)新建一個web客戶端項目,將生成的客戶端代碼拷貝到src下。

3)調用web服務

Ø  方法一:使用標準的JAX-WS的API完成客戶端調用(不須要導入任何CXF包)

//注意。此處http://service.jaxb.com/來源於wsdl文件中targetNamespace

QName qName =

 newQName("http://service.jaxb.com/","HelloServiceImplService");

HelloServiceImplService helloServiceImplService =

new HelloServiceImplService(

new URL("http://localhost:8080/WSCXF/helloService?wsdl"),qName);

IHelloService helloService

=(IHelloService)helloServiceImplService.getPort(IHelloService.class);

Ø  方法二:使用CXF中JaxWsProxyFactoryBean客戶端代理工廠調用web服務(須要導入CXF相關包)

JaxWsProxyFactoryBean soapFactoryBean = newJaxWsProxyFactoryBean();

soapFactoryBean.setAddress("http://localhost:8080/WSCXF/helloService");

soapFactoryBean.setServiceClass(IHelloService.class);

Object o = soapFactoryBean.create();

IHelloService helloService = (IHelloService)o;

Ø  方法三:

String endPointAddress = "http:// localhost:8080/WSCXF/helloService";

Service service = Service.create(

newQName("http://service.jaxb.com/","IHelloService"));

service.addPort(

new QName("http://service.jaxb.com/","IHelloServicePort");,

SOAPBinding.SOAP11HTTP_BINDING, endPointAddress);

IHelloService helloService =service.getPort(IHelloService.class);

Ø  方法四:(須要導入CXF相關包)

JaxWsDynamicClientFactory dcf =JaxWsDynamicClientFactory.newInstance();

org.apache.cxf.endpoint.Client client =

dcf.createClient("http://127.0.0.1:8080/WSCXF/helloService?wsdl");

//sayHello 爲接口中定義的方法名稱  張三爲傳遞的參數   返回一個Object數組

Object[] objects=client.invoke("sayHello","張三");

//輸出調用結果

System.out.println(objects[0].toString());

7.CXF和Spring整合

CXF能夠很好的與Spring整合,而後發佈在tomcat下,只須要簡單的Spring配置便可。

7.1 服務端開發

1)新建web項目,並添加相關包。(包括spring和cxf相關包)

2)寫好一個接口和實現類。(見demo)

3)新建beans.xml文件。

<?xml version="1.0"encoding="UTF-8"?>

<beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd">

 

<!--spring發佈web服務配置 -->

<importresource="classpath:META-INF/cxf/cxf.xml" />

<importresource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

<importresource="classpath:META-INF/cxf/cxf-servlet.xml" />

 

<bean id="helloService"class="com.jaxb.service.HelloServiceImpl" />

<!--

<bean id="helloService"class="com.aegis.service.HelloServiceImpl" />

-->

<!--endpoint 方式發佈web服務和 server方式同樣 -->

<!--

<jaxws:endpointid="helloServiceWs" address="/helloService"

     implementor="#helloService"/>

-->

<!--

     另外一種寫法,建議不要用這種方法 ,若是實現類有的屬性要經過spring依賴注入的話,

     這種方法只是簡單的new個實現類,他的屬性沒有經過spring依賴注入給注入值

-->

<!--

<jaxws:endpointid="helloServiceWs" address="/helloService"

     implementor="com.jaxb.service.HelloServiceImpl"/>

-->

 

<!—下面這個是wss4j的配置,後面會講到相關知識,須要配置在spring配置文件中 -->

<!--wss4j 服務端配置 -->

<bean id="wss4jInInterceptor"

class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">

     <constructor-arg>

         <map>

             <entrykey="action" value="UsernameToken" />

             <!--<entry key="passwordType" value="PasswordText" />-->

             <!--密碼使用MD5密文發送 -->

             <entrykey="passwordDigest" value="PasswordText" />

             <entrykey="passwordCallbackClass"

                 value="com.security.service.ServerPasswordCallbackHandler"/>

         </map>

     </constructor-arg>

</bean>

 

<jaxws:serverid="helloServiceWs" address="/helloService">

     <jaxws:serviceBean>

         <refbean="helloService" />

     </jaxws:serviceBean><!--使用這種方法發佈web服務 -->

     <jaxws:inInterceptors>

         <refbean="wss4jInInterceptor" />

     </jaxws:inInterceptors><!—wss4j配置 -->

     <!--<jaxws:serviceFactory>

         <refbean="jaxWsServiceFactoryBean" />

     </jaxws:serviceFactory>  --><!—數據綁定方式配置 -->

</jaxws:server>

<!-- 經過Spring建立數據綁定的類-->

   <!--<bean id="aegisBean"class="org.apache.cxf.aegis.databinding.AegisDatabinding" />

   <bean id="jaxWsServiceFactoryBean"

class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean">

       <property name="wrapped" value="true" />

       <property name="dataBinding" ref="aegisBean"/>

   </bean> -->

</beans>

4)配置web.xml

<context-param>

     <param-name>contextConfigLocation</param-name>

     <param-value>/WEB-INF/beans.xml</param-value>

</context-param>

<listener>

<listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

 

<!—在tomcat中發佈須要配置servlet -->

<servlet>

     <servlet-name>CXFServlet</servlet-name>

     <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

     <load-on-startup>2</load-on-startup>

</servlet>

<servlet-mapping>

     <servlet-name>CXFServlet</servlet-name>

     <url-pattern>/ws/*</url-pattern>

</servlet-mapping>

5)發佈web項目

由於在web.xml裏面配置了servlet,則能夠將項目發佈到tomcat下,啓動tomcat便可。

6)訪問wsdl

http://localhost:8080/WSCXF/ws/helloService?wsdl

7.2 客戶端調用

1)新建一個web客戶端項目,用wsdl2java生成客戶端代碼。將生成的客戶端代碼拷貝到src下。添加spring相關包。

2)配置spring配置文件。

beans.xml存放在src目錄下,具體配置以下:

<?xml version="1.0"encoding="UTF-8"?>

<beansxmlns="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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd">

 

<!-- wss4j 配置在客戶端,後面有講到相關知識 -->

<!--wss4j 客戶端配置 -->

<beanid="wss4jOutInterceptor"

class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">

     <constructor-arg>

         <map>

             <entrykey="action" value="UsernameToken" />

             <entrykey="user" value="Fetion" />

             <!--<entry key="passwordType" value="PasswordText" />-->

             <!--密碼使用MD5密文發送 -->

             <entrykey="passwordDigest" value="PasswordText" />

             <entrykey="passwordCallbackClass"

                 value="com.security.client.ClientPasswordCallbackHandler"/>

         </map>

     </constructor-arg>

</bean>

<jaxws:client id="helloServeiceClient"

     address="http://localhost:8080/WSCXF/ws/helloService"serviceClass="com.jaxb.client.IHelloService">

     <jaxws:outInterceptors>

         <refbean="wss4jOutInterceptor" />

        </jaxws:outInterceptors><!--wss4j客戶端配置-->

</jaxws:client>

</beans>

 

2)調用web服務

在main方法中運行:

ClassPathXmlApplicationContext app = newClassPathXmlApplicationContext("beans.xml");//此處beans.xml放在src下,也須要放在其餘目錄下,但須要註明清楚

//獲取webservice服務的操做接口

IHelloServicehelloService = (IHelloService)app.getBean("helloServeiceClient");

相關文章
相關標籤/搜索