CXF建立webservice客戶端和服務端

執筆記憶的空白發表於2014-04-09

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)的本質,就是通過網路呼叫其他網站的資源。

舉例來說,寫一個“四川大地震圖片牆”,它能動態顯示關於四川地震的最新圖片。但是,所有的圖片都不是儲存在自己的伺服器上,而是來自flickr.com。只需要發出一個動態請求,要求flickr.com向自己提供圖片。這種情況下,flickr.com提供的就是一種Web service。如果把圖片都存放在本地伺服器,不呼叫flickr.com,那麼我就是在使用“本地服務”。

所以,Webservice讓你的網站可以使用其他網站的資源,比如在網頁上顯示天氣、地圖、twitter上的最新動態等等。

2.Web Service架構和雲

如果一個軟體的主要部分採用了“網路服務”,即它把儲存或計算環節“外包”給其他網站了,那麼我們就說這個軟體屬於Web Service架構。

Web Service架構的基本思想,就是儘量把非核心功能交給其他人去做,自己全力開發核心功能。比如,如果你要開發一個相簿軟體,完全可以使用Flickr的網路服務,把相片都儲存到它上面,你只要全力做好相簿本身就可以了。總體上看,凡是不屬於你核心競爭力的功能,都應該把它“外包”出去。

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

3.Web Service的優勢

Web Servcie最主要的優點是,使用不同程式和在不同系統平臺上開發出來的程式,都可以相互通訊。SOAP作為Web Service的基本通訊協議,簡單,投入和使用的代價也很小。Web Service使用標準的網際網路協議-XML、HTTP和TCP/IP。

Web Service有以下的優越性:

1)平臺無關。不管你使用什麼平臺,都可以使用Web service。

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");



原文出處:

作者:永恆の_☆ 地址:http://blog.csdn.net/chenghui0317/article/details/9320053

相關文章