Spring與XFire

概述

XFire是與Axis 2並列的新一代Web Service框架,通過提供簡單的API支持Web Service各項標準協議,幫助你方便快速地開發Web Service應用。XFixe內建在STAX的基礎上之上,STAX是基於流的XML解析引擎,這使得XFire擁有很高的性能。

相對Axis來說,目前它的人氣指數相當高,加上其提供了和String集成的支持,在目前的Web Service開源社區擁有衆多的追隨者,XFire被多個開源項目所使用。XFire爲Spring提供了支持,這使得我們可以很容易在Spring中 使用XFire構建Web Service應用。本文講述瞭如何使用XFire在Spring中開發Web Service的具體過程。

XFire特性

XFire是Web Service框架的後起之秀,它從現有的框架中借鑑了許多優秀的理念,力爭將Web Service的應用開發難度降到最低。此外,還提供了各種綁定技術、支持多種傳輸協議,對Web Service體系中許多新的規範提供了支持。簡單來說,它具有以下一些特性:

  • 支持重要的Web Service規範,如SOAP、WSDL、WS-I Basic Profile,、WS-Addressing、WS-Security等;
  • 高性能的SOAP 棧設計;
  • 可插拔的綁定,支持POJO、XMLBeans、JAXB1.1、JAXB2以及Castor;
  • 支持JSR 181規範,該規範通過JDK 5.0註解將POJO導出爲Web Service;
  • 支持多種傳輸協議:HTTP、JMS、XMPP、In-JVM等,其中In-JVM允許我們在不啓動Web應用器的情況下,象一般的程序一樣測試Web Service應用;
  • 易用的API,XFire API簡潔明瞭,便於使用;
  • 支持Spring、Pico、Plexus、Loom等容器;
  • 支持JBI(Java Business Integration:Java 業務整合)規範,JBI是JSR 208的實現;
  • 能夠根據WSDL創建客戶端和服務器端的存根代碼;
  • 率先對JAX-WS提供了支持,JAX-WS是JAX-RPC的替代者。

XFire 體系及重要API

ServiceFactory

ServiceFactory是XFire的核心類,它可以將一個POJO生成爲一個Web Service。讓我們通過一個最簡單的例子瞭解ServiceFactory的用途,假設我們在一個POJO中定義了一個業務,現在希望將其導出爲 Web Service,通過ServiceFactory可以輕而易舉地達到目的:

XFire xfire = XFireFactory.newInstance().getXFire();

ServiceFactory factory = new ObjectServiceFactory(xfire.getTransportManager(), null);

Service service = factory.create(YourService.class);

這樣我們就爲YourService類創建基於SOAP 1.1封裝的Web Service。Service的輸入輸出參數如果爲簡單類型的對象(由String、int、long等基本類型組成),無須進行額外的映射設置,對於 複雜類型的輸入輸出Service,XFire將會自動嘗試將其序列化(通過Aegis綁定)。緊接着,你就可以註冊這個Service。

xfire.getServiceRegistry().register(service);

XFire推薦通過一個接口開放服務,此時,你可以指定一個具體的實現類:

service.setProperty(ObjectInvoker.SERVICE_IMPL_CLASS, YourServiceImpl.class);

Handler

一個Handler可以看成是XFire的一個加工套件,XFire通過它們定義SOAP發送和接收之前的各種加工處理邏輯。如Handler可以 對SOAP體的內容進行加工處理,或者SOAP頭進行處理。可以簡單地通過擴展AbstractHandler定義一個自己的Handler類:

import org.codehaus.xfire.MessageContext;

import org.codehaus.xfire.handler.AbstractHandler;

public class YourHandler extends AbstractHandler

{

public void invoke(MessageContext context)

{

// Do Sth...

}

}

Handler可以註冊到Service或Transport(代表SOAP輸入輸出的的傳輸對象)中,在服務請求和響應管道里,Service和Transport註冊的Handler將執行額外的處理操作。你可以按如下方式註冊Handler:

Servic s = factory.create(YourService.class);

s.addInHandler(new YourHandler1());①添加一個Handler,對輸入SOAP進行處理

s.addOutHandler(new YourHandler2());②添加一個Handler,對輸出SOAP進行處理

s.addFaultHandler(new YourHandler3());③添加一個Handler,對錯誤SOAP進行處理

下面的Handler對SOAP頭進行處理,添加一些特定的信息:

import org.codehaus.xfire.MessageContext;

import org.codehaus.xfire.handler.AbstractHandler;

public class YourHandler extends AbstractHandler

{

public QName[] getUnderstoodHeaders()

{

return new QName[] { new QName("YourHeader", "urn:your:header:ns") };

}

}

在管道中以流方式處理SOAP

XFire是完全基於流數據處理進行工作的系統,這意味着XFire不是將整個SOAP文檔緩存在內存中,而是以管道的方式接收SOAP流數據。這種工作方式的轉變帶來了可觀的性能回報,同時節省了內存的佔用。

對於習慣了Axis、GLUE等這些基於DOM處理模型Web Service框架的開發者來說,需要一些時間來適應這種轉變。

XFire從管道中接收一個SOAP請求到返回一個SOAP響應,會經歷一系列的階段。在管道調用的任何一個階段,XFire都可以添加一些額外的 Handler,在對消息進行加工處理後再傳入到下一個階段中。圖1展示了XFire管道從接收SOAP請求到返回SOAP響應所經歷的所有階段:

在Spring中使用XFire構建Web Service應用

1 XFire Web Service請求和響應的過程

在SOAP請求消息對Web Service發起真正調用之前,分別會經過傳輸(Transport)、預轉發(PreDispatch)、轉發(Dispatch)、策略實施 (Policy)、用戶信息處理(User)、預調用(PreInvoke)、服務調用(Service Invocation)等階段。當,Web Service調用後,XFire生成響應SOAP消息並通過管道發送給客戶端請求者,這一過程會先後經歷調用後(PostInvoke)、用戶信息處理 (User)、策略實施(Policy)、傳輸(Transport)這四個階段。每一個階段都是一個可控點,通過編寫並註冊一些相應的Handler就 可以實施一些額外處理邏輯,如審計、SOAP消息加密、簽名、壓縮等。

將POJO Bean導出爲Web Service

通過XFire爲Spring提供的服務導出器可以輕鬆地將POJO導出爲標準的Web Service,此外,XFire還允許我們使用JSR 181註解對POJO進行標註,無需使用XML配置就可以導出爲Web Service,各種複雜的轉換細節被巧妙地隱藏在XFire之中。

使用導出器導出Web Service

XFire爲Spring提供了方便易用的導出器XFireExporter,藉助XFireExporter的支持,我們可以在Spring容器 中將一個POJO導出爲Web Service。BbtForum是Baobaotao論壇業務服務類,它擁有衆多的業務方法,我們現在希望將其提供查詢最近幾天精華帖子數的業務方法開 放爲Web Service。爲了避免過多地開放不必要的接口方法,需要定義了一個BbtForumService窄接口,它定義那些需要開放爲Web Service的業務方法:

package com.baobaotao.xfire.server;

public interface BbtForumService {

int getRefinedTopicCount(int lastDay);①查詢最近幾天論壇精華帖子數的服務接口

}

將一個業務類所有需要開放爲Web Service的方法通過一個窄接口來描述是值得推薦的作法,這讓Web Service的接口顯得很“乾淨”。其次,XFire的導出器也需要服務接口的支持,因爲它採用基於接口的動態代理技術。真實的業務類當然需要實現 Web Service窄接口:

package com.baobaotao.service;

import javax.jws.WebService;

import com.baobaotao.xfire.server.BbtForumService;

public class BbtForum implements BbtForumService{①實現Web Service窄接口

public int getRefinedTopicCount(int lastDay) {②該方法在窄接口中定義,導出爲Web Service服務

if(lastDay <= 2) return 10;

else if(lastDay <= 5) return 20;

else return 32;

}

}

BbtForum中的方法在真實的系統中應該引用其它的業務類或DAO獲取數據庫中的真實數據,爲了簡化實例,我們通過一段簡單的代碼進行模擬,如②所示。

在擁有了窄接口之後中,剩餘的工作就是在Spring配置文件中通過XFireExporter將BbtForum#getRefinedTopicCount()方法導出爲Web Service,具體配置如代碼清單1所示:

代碼清單1 applicationContext.xml:將POJO導出Web Service

<beans>

①引入XFire預配置信息

<import resource="classpath:org/codehaus/xfire/spring/xfire.xml" />

②使用XFire導出器

<bean id="BbtForumService" class="org.codehaus.xfire.spring.remoting.XFireExporter">

<property name="serviceFactory" ref="xfire.serviceFactory" />②-1:引用xfire.xml中定義工廠

<property name="xfire" ref="xfire" />②-2:引用xfire.xml中定義的xfire實例

<property name="serviceBean" ref="bbtForum" />②-3:業務服務Bean

<property name="serviceClass"②-4:業務服務Bean的窄接口類

value="com.baobaotao.xfire.server.BbtForumService" />

<property name="name" value="BbtForumServiceUT"/>②-5:Web Service名稱

</bean>

<bean id="bbtForum" class="com.baobaotao.service.BbtForum" />

</beans>

上面的配置將BbtForum所有定義在BbtForumService窄接口中的方法導出爲Web Service。在XFire核心JAR包中擁有一個預定義的Spring配置文件,它定義了XFire在Spring中必須用到的一些Bean和資源, 需要引入這個預定義的配置文件,如①所示。緊接着,就可以使用XFireExporter將業務類導出爲Web Service了。②-1、②-2爲導出器引入XFire環境,對於任何導出器,這都是標準的配置,所以如果有多個導出器,可以將這兩個屬性通過一個父 <bean>標籤進行抽象。而②-3、②-4分別定義了業務服務類及需要導出爲Web Service方法的窄接口。Web Service的默認名稱是窄接口的類名,即BbtForumService,你可以通過name屬性顯式指定Web Service的名稱,如②-5所示。

通過這個簡單的配置,就完成了將業務服務類開放爲Web Service的工作,接下來,我們就可以通過配置Web 服務器的web.xml,將其通過HTTP傳輸協議開放出去。

配置web.xml

一般情況下,我們通過HTTP作爲Web Service的傳輸協議,這樣你只需啓動一個Web服務器(如Tomcat),客戶端就可以通過HTTP訪問到Web Service服務了。爲了集成Spring容器,XFire專門提供一個XFireSpringServlet,我們可以在web.xml中配置該 Servlet,將Spring容器中定義的Web Service在某個URI下發布,如代碼清單2所示:

代碼清單2 web.xml配置

<web-app>

<context-param>

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

<param-value>classpath:applicationContext.xml</param-value>①剛纔配置的Spring文件

</context-param>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<servlet>②配合Spring容器中XFire一起工作的Servlet

<servlet-name>xfireServlet</servlet-name>

<servlet-class>org.codehaus.xfire.spring.XFireSpringServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>xfireServlet</servlet-name>

<url-pattern>/service/*</url-pattern>③在這個URI下開放Web Service服務

</servlet-mapping>

</web-app>

首先,我們指定在代碼清單1中所配置的Spring配置文件,如①所示。然後定義一個XFireSpringServlet,讓其截取所有/service URI下的請求,如②和③所示。

創建一個Tomcat配置文件baobaotao.xml,並編寫一行映射配置(這裏使用Tomcat 5.5),這種方式無須將Web應用打包成WAR,方便開發測試:

<Context path="/baobaotao" docBase="D:/masterSpring/chapter16/webapp"/>

將baobaotao.xml放置到<TOMCAT_HOME>/conf/Catalina/localhost 目錄下,啓動Tomcat服務,鍵入http://localhost: 8080/baobaotao/service/BbtForumService?wsdl,你將可以看到BbtForumService對應的 WSDL,如圖2所示:

在Spring中使用XFire構建Web Service應用

2 BbtForumService的WSDL

閱讀這個WSDL文檔,我們可以知道BbtForum的getRefinedTopicCount已經被成功地發佈爲Web Service了。只要拿到這個WSDL就可以開發相應的客戶端調用程序了。

使用JSR 181註解導出Web Service

前面兩小節中,我們領教了XFireExporter導出器的威力。在需要導出爲Web Service的業務類數目不大時,XFireExporter的配置方式非常優雅。但是,如果有很多需要導出爲Web Service的業務類,你必須分別爲它們配置一個XFireExporter,這讓我們回憶起了 TransactionProxyFactoryBean(每一個需要事務功能的業務類需要分別配置)。在學習過@Transaction註解後,我們自 然而然地希望使用類似註解技術完成Web Service導出的工作。

JSR 181就是爲此目的而提出的,它是BEA領導的一個Web Service規範。XFire已經支持JSR 181 2.0,你既可以使用JDK 5.0的註解,也可以在JDK 5.0之前的版本中使用commons-attributes註解。

使用JSR 181的明顯好處是,你僅需在業務類和窄接口標註JSR 181註解,不管你有多少需要導出爲Web Service的業務類,僅須在Spring中配置一個XFire提供的JSR 181註解增強Bean就可以了。

註解增強處理器會對Spring容器中所有標註JSR 181註解的業務類進行處理,並分別將它們導出爲Web Service。使用JSR 181時,必須將XFire的依賴類庫xfire-jsr181-api-1.0-M1.jar添加到類路徑中。

如果輸入、輸出的對象類型僅包括基本類型的屬性,僅需要在業務類和窄接口中分別使用@WebService註解進行簡單的配置就可以了,XFire將根據默認約定導出Web Service。

窄接口僅需要定義一個@WebService註解,並指定SOAP的命名空間就可以了:

package com.baobaotao.xfire.server;

import javax.jws.WebService;

@WebService(targetNamespace = "http://www.baobaotao.com")①指定SOAP的命名空間

public interface BbtForumService {

int getRefinedTopicCount(int lastDay);

}

XFire應用JSR 181比較怪的一點是,除了需要在窄接口中提供註解外,在實現業務類中也需要提供相應的註解:

package com.baobaotao.service;

import javax.jws.WebService;

import com.baobaotao.xfire.server.BbtForumService;

@WebService(serviceName = "BbtForumService",①指定導出的Web Service名稱

endpointInterface = "com.baobaotao.xfire.server.BbtForumService")②對應的窄接口

public class BbtForum implements BbtForumService{

}

如果碰到以下應用場景:輸入、輸出對象是複雜的對象(如未使用泛型的集合類),當返回類型是一個對象但不希望輸出所有的結果,或者不希望使用默認的屬性名。

這時可以在業務方法中通過@WebMethod、@WebResult等註解提供額外的信息來達到目的。更多關於JSR 181的信息請參考:http://dev2dev.bea.com/webservices/jwsm.html。

按照相似的方式,可以爲應用中其它的業務類進行Web Service標註,在完成標註後,需要在Spring配置中啓用XFire JSR 181處理器,對Spring容器中所有標註@WebService的Bean進行統一的處理,以便執行真正Web Service的導出工作。XFire在Spring中對應的配置如下所示:

代碼清單3 applicationContext.xml:使用JSR 181導出Web Service

<beans >

<import resource="classpath:org/codehaus/xfire/spring/xfire.xml" />

<bean id="webAnnotations"①該Bean獲取Spring容器中所有標註@WebService的Bean

class="org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations" />

<bean id="jsr181HandlerMapping"②對標註@WebService的Bean進行處理,完成導出工作

class="org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping">

<property name="xfire" ref="xfire" />

<property name="webAnnotations" ref="webAnnotations" />

</bean>

③該Bean標註了@WebService註解

<bean id="bbtForum" class="com.baobaotao.service.BbtForum" />

</beans>

重啓Tomcat,查看http://localhost:8080/baobaotao/service/BbtForumService?wsdl,你依舊可以看到如圖2 BbtForumService的WSDL所示的WSDL。

各種客戶端調用方式

XFire爲訪問服務端Web Service提供了各種方便的方式:在可以獲取服務端窄接口類的情況下,可以根據服務地址和窄接口類創建客戶調用程序。

如果不能獲得服務窄接口類,XFire允許你通過WSDL文件生成客戶端調用程序,通過指定服務接口的方式調用服務。鑑於這種調用方式不夠面向對象,XFire提供了一個根據WSDL生成客戶端存根代碼的工具,這樣你就可以方便以面向對象的方式編寫客戶端程序了。

使用服務端的窄接口類

如果客戶端可以獲取服務端的Web Service的窄接口類,這時可以使用XFire的ObjectServiceFactory將對應地址的Web Service轉換爲窄接口實例進行調用,如代碼清單4所示:

代碼清單4使用窄接口調用Web Service應用

package com.baobaotao.xfire.client;

import org.codehaus.xfire.client.XFireProxyFactory;

import org.codehaus.xfire.service.Service;

import org.codehaus.xfire.service.binding.ObjectServiceFactory;

import com.baobaotao.xfire.server.BbtForumService;

public class WithClassClient {

public int getRefinedTopicCount() {

①根據窄接口創建Service模型

Service serviceModel = new ObjectServiceFactory().create(BbtForumService.class);

②服務對應URL地址

String serviceURL = "http://localhost:8080/baobaotao/service/BbtForumService";

BbtForumService service = null;

try {

③將Web Service轉換爲窄接口實例

service = (BbtForumService) new XFireProxyFactory().

create(serviceModel, serviceURL);

} catch (Exception e) {

throw new RuntimeException(e);

}

return service.getRefinedTopicCount(20);④調用Web Service方法

}

public static void main(String[] args) {

WithClassClient client = new WithClassClient();

System.out.println("topic count is:"+client.getRefinedTopicCount());

}

}

XFire根據Service模型對象及Web Service的URL地址就可以構造出Web Service的調用實例。在服務端Tomcat啓動的情況下,運行以上的客戶端代碼,將可以獲得正確的輸出。

使用WSDL文件構造客戶端程序

並不是任何時候都可以獲得Web Service服務端的窄接口類,但我們必然可以獲取Web Service對應的WSDL文檔。XFire允許我們僅通過Web Service對應的WSDL文件構造客戶端訪問程序。

這無疑給創建客戶端程序帶來了極大的便利性,你可以直接通過URL指定WSDL,也可以將WSDL保存在本地系統中,通過InputStream的方式獲取WSDL內容。下面,我們使用InputStream的方式提供WSDL:

代碼清單5通過WSDL創建客戶端程序

package com.baobaotao.xfire.client;

import java.net.URL;

import org.codehaus.xfire.client.Client;

public class OnlyWsdlClient {

public int getRefinedTopicCount() {

try {

String wsdl = "com/baobaotao/xfire/client/BbtForumService.wsdl";①對應的WSDL文件

Resource resource = new ClassPathResource(wsdl);

Client client = new Client(resource.getInputStream(), null);②根據WSDL創建客戶實例

③調用特定的Web Service方法

Object[] results = client.invoke("getRefinedTopicCount",new Object[]);

return (Integer) results[0];

} catch (Exception e) {

throw new RuntimeException(e);

}

}

public static void main(String[] args) {

OnlyWsdlClient client = new OnlyWsdlClient();

System.out.println("topic count is:" + client.getRefinedTopicCount());

}

}

你可以通過http://localhost:8080/baobaotao/service/BbtForumService?wsdl地址獲取BbtForumService對應的WSDL,並將其保存在工程項目的src對應的類包目錄:com/baobaotao/xfire/client/BbtForumService.wsdl。

我們通過Spring的ClassPathResource讀取BbtForumService.wsdl,XFire從Resource中獲取 WSDL的輸入流並生成一個客戶端實例。接着,我們就可以通過這個客戶端實例,指定Service服務名和輸入參數調用Web Service的服務方法了,如③所示。

可能會有讀者認爲這種完全根據WSDL創建客戶端程序的方式會帶來低劣的運行性能,筆者通過測試發現,確實會造成一定的性能降低,但也不象想象中那樣低效。

使用基於窄接口的客戶端程序和使用基於WSDL的客戶端程序訪問一次BbtForumService的時間依次是1300 ms和1450 ms。如果WSDL文檔很複雜,由於需要解析整個WSDL文檔,這種客戶端程序的性能會受到更多的挑戰。不過,如果只要在程序中緩存Client實例,由 於創建Client的代價是一次性的,性能問題就可以忽略了。

根據WSDL生成客戶端代碼

XFire允許通過運行Ant任務,根據WSDL文件生成訪問Web Service的客戶端代碼存根,同時XFire還提供了一個Eclipse插件完成相同的任務。本節裏,我們將學習通過XFire Eclipse插件生成BbtForumService客戶端存根代碼的知識。

安裝Eclipse XFire 插件

1.Help->Software Updates->Find and Install...

2.選擇“Search for new features to install”,並點擊Next;

3.選擇“New Remote Site...”,創建一個Name爲XFire,URL爲

http://dist.codehaus.org/xfire/update/的網站;

4.點擊Finish安裝XFire插件。

使用插件創建客戶端代碼存根

  • File->New->Other...->XFire->Code generation from WSDL document;
  • 彈出一個對話框,如圖3所示:

在Spring中使用XFire構建Web Service應用

3創建客戶端代碼存根

指定WSDL文件的位置,存根代碼的輸出地址及對應的類包,點擊Finish。

  • XFire插件將在生成客戶端代碼存根的同時生成服務端代碼的存根,如下圖所示:

在Spring中使用XFire構建Web Service應用

圖4生成的代碼

BbtForumServiceClient是BbtForumServicePortType的工廠類,它提供了若干個獲取 BbtForumServicePortType實例的重載方法。BbtForumServicePortType對應服務端的窄接口 BbtForumService類。而BbtForumServiceImpl是服務端的存根代碼,在META-INF中還有XFire的服務配置文件。 對於客戶端來說,一般不需要服務端的代碼,所以你可以將BbtForumServiceImpl和META-INF刪除。

下面,我們利用XFire生成的BbtForumServiceClient對服務端的Web Service進行調用:

package com.baobaotao.xfire.client;

public class StubClient {

public static void main(String[] args) {

BbtForumServiceClient client = new BbtForumServiceClient();

String serviceUrl = "http://localhost:8080/baobaotao/service/BbtForumService";

①獲取對應服務窄接口實例

BbtForumServicePortType portType = client.getBbtForumServiceHttpPort(serviceUrl);

int count = portType.getRefinedTopicCount(20);②對服務進行調用

System.out.println("count:" + count);

}

}

我們首先實例化一個BbtForumServiceClient,然後通過URL指定Web Service的服務地址,然後創建一個服務的窄接口實例,如①所示,接着我們就可以使用這個窄接口實例進行Web Service服務的調用了。

Web Service的測試

在上一節裏,我們學習了客戶端調用服務端中Web Service的方法。在實際應用中,在開放Web Service之前需要進行嚴格的測試,以保證功能的正確性。在一般框架中,測試Web Service往往這是一個煉獄般痛苦的過程。

XFire通過AbstractXFireTest極大地簡化Web Service的測試。AbstractXFireTest允許我們無需構造客戶端調用程序,在SOAP報文層面開展對服務端代碼的測試, AbstractXFireTest提供了一系列方便的方法對SOAP報文進行驗證。XFire還特別爲Spring環境下進行Web Service測試提供了一個AbstractXFireSpringTest子類,僅通過啓用Spring容器就可以完成Web Service的測試。通過XFire精心設計的測試工具類,對Web Service的測試工作已經是一項可以輕鬆應對的工作。

如果你在編寫服務端Web Service的同時,還需要編寫客戶端調用程序,這時不可避免的,你希望從客戶端角度對Web Service進行測試。由於客戶端程序需要訪問真實的Web Service,所以需要開啓Web服務器,讓服務端的Web Service能夠提供服務共客戶端訪問調用。如果客戶端和服務端都在同一個項目中開發,XFire允許你在不啓動Web服務器的情況下測試客戶端程序, 其原理是讓Web Service運行於JVM模式下。

基於SOAP報文的純服務端測試

AbstractXFireTest擴展於JUnit標準的TestCase類,提供了向某個Web Service發送SOAP請求報文並對返回的SOAP響應報文進行檢驗的能力。該測試類提供了若干個方便的斷言方法,分別介紹如下:

  • void assertNoFault(Document node):確認SOAP響應報文無錯誤;
  • java.util.List assertValid(java.lang.String xpath, java.lang.Object node):確認在DOM節點特定路徑下有對應的元素,路徑通過XPath表達式進行定義,該方法還將匹配的元素以List對象返回,你可以對匹配的元素 進行進一步的檢驗;
  • void assertXPathEquals(java.lang.String xpath, java.lang.String value, Document node):確認特定路徑DOM節點爲某一特定值;
  • assertInvalid(java.lang.String xpath, java.lang.Object node):確認DOM節點特定路徑下未包含元素。

AbstractXFireSpringTest是AbstractXFireTest的子類,在Spring中你僅需要擴展該類並實現該類的抽象 方法ApplicationContext createContext(),就可以對Spring容器中用XFire定義的Web Service進行測試了。

爲了測試Web Service,我們必須準備一個SOAP請求報文,你可以簡單地手工編寫一個,或通過SOAP報文截取工具(如前面我們介紹的TcpTrace、 SOAPScope、Apache Axis的TCPMon等)獲得一些可用的SOAP請求報文。下面是一個訪問BbtForumService 服務的請求SOAP報文:

代碼清單6 request_soap.xml:SOAP報文層面測試

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<soap:Body>

<getRefinedTopicCount

xmlns="http://server.xfire.baobaotao.com">

<in0 xmlns="http://server.xfire.baobaotao.com">20</in0>

</getRefinedTopicCount>

</soap:Body>

</soap:Envelope>

我們將其保存在request_soap.xml文件中放置在類路徑com/baobaotao/xfire/server下。當該SOAP請求報 文發送給BbtForumService的Web Service後,我們預計它應該返回對應代碼清單7所示的正確的SOAP響應報文:

代碼清單7 SOAP響應報文

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

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<soap:Body>

<getRefinedTopicCountResponse xmlns="http://www.baobaotao.com">

<out>32</out>

</getRefinedTopicCountResponse>

</soap:Body>

</soap:Envelope>

下面,我們着手編寫測試BbtForumService Web Service的測試類,以驗證實際SOAP響應報文是否和代碼清單7中的一樣:

代碼清單8 TestBbtForumService

package com.baobaotao.xfire.server;

import org.codehaus.xfire.spring.AbstractXFireSpringTest;

import org.jdom.Document;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestBbtForumService extends AbstractXFireSpringTest {

protected ApplicationContext createContext() {①創建包含Web Service的Spring容器

return new ClassPathXmlApplicationContext(

new String[]);

}

public void testUsingSoapRequest() throws Throwable {

②通過SOAP請求報文訪問BbtForumService的Web Service,對返回

SOAP響應報文進行檢測

Document response = invokeService("BbtForumService",

"/com/baobaotao/xfire/server/request_soap.xml");

assertNoFault(response);③確認不包含錯誤

④爲命名空間指定一個別名,方便後續XPath表示式的編寫

addNamespace("k", "http://www.baobaotao.com");

assertValid("//soap:Body/k:getRefinedTopicCountResponse", response);

assertXPathEquals("//k:getRefinedTopicCountResponse/k:out/text()", "32",response);

printNode(response);⑦打印響應報文,以便肉眼查看

}

}

使用AbstractXFireSpringTest測試Web Service首先要做的第一件事是通過實現createContext()方法構造Spring容器,如①所示。當Spring容器啓動時,XFire 將自動讓容器中的Web Service生效(僅進行測試,不能對外提供服務)。

第二步需要向Web Service發送一個SOAP請求報文以得到一個SOAP響應報文,如②所示。接下來,就是通過AbstractXFireTest提供的檢測DOM內 容的方法對報文進行正確性驗證。由於代碼清單7的SOAP報文體中對應的<getRefinedTopicCountResponse>元素 及內部元素都位於http://www.baobaotao.com命名空間中,報文體中沒有爲這個命名空間定義相應的別名,爲了在後續斷言方法中能夠使 用簡單的方式定義XPath表達式,我們在④處爲http://www.baobaotao.com命名空間定義了一個別名。

理解以上測試代碼中幾個斷言方法的關鍵在於理解XPath表達式語言,XPath語法內容很豐富,不可能在這裏逐一講解,我們在這裏介紹一些典型的XPath語法以滿足常見的測試需求:

  • 以“/”爲前綴的路徑表示從DOM根路徑開始,如“/soap:Envelope/soap:Body”;
  • 以“//”爲前綴的路徑表示從DOM任意元素開始查詢,如“//out”表示任意元素爲out的元素;
  • 元素的屬性通過@attrName表示,如“//xsd:complexType[@name="Book"]”表示DOM中任意元素名爲complexType並且擁有一個值爲Book的name屬性的元素;
  • 元素的值通過text()表示,如“//test:Response[text()='32']”表示DOM中任意值爲32,元素名爲Response,且位於test命名空間中的元素。

現在回過頭來看⑤、⑥兩處的斷言方法,相信大家就可以很容易地理解斷言規則了,⑤處的斷言檢測SOAP響應報文是否包含某一特定元素,而⑥處的斷言則對元素內的值進行檢測。我們也可以通過printNode()方法將一個節點打印到控制檯上,方便肉眼查看。

在JVM模式通過客戶端進行測試

能夠不啓動Web服務器的情況下通過客戶端程序測試Web Service的功能,這一嶄新的測試方法對於開發人員來說一定深具吸引力。因爲,這意味着你可以完全在IDE環境中運行測試,不需要外部環境的支持。

不過享受這一測試好處的應用必須保證客戶端和服務端的Web Service都位於同一JVM中,這時請求報文和響應報文直接在JVM 內部通道中傳輸。當使用JVM內部通道傳輸請求和響應的SOAP報文時,我們僅需要調整服務的地址就可以了:

代碼清單9 TextBbtForumService:JVM模式測試

package com.baobaotao.xfire.client;

import org.codehaus.xfire.client.XFireProxyFactory;

import org.codehaus.xfire.service.Service;

import org.codehaus.xfire.service.binding.ObjectServiceFactory;

import org.codehaus.xfire.spring.AbstractXFireSpringTest;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baobaotao.xfire.server.BbtForumService;

public class TextBbtForumService extends AbstractXFireSpringTest {

protected ApplicationContext createContext() {

return new ClassPathXmlApplicationContext(

new String[]);

}

public void testGetRefinedTopicCount() throws Throwable {

Service serviceModel = new ObjectServiceFactory().create(BbtForumService.class);

XFireProxyFactory factory = new XFireProxyFactory(getXFire());

①在JVM內部通道中進行SOAP請求和響應報文的傳輸,請注意粗體所示的服務地址

BbtForumService service = (BbtForumService) factory.create(serviceModel,

"xfire.local://BbtForumService");

int count = service.getRefinedTopicCount(20);

assertEquals(count,32);

}

}

以上代碼中,①處的服務地址採用了JVM模式的地址,和其對應的HTTP地址則是http://localhost:8080/baobaotao/service/BbtForumService,所以僅需將服務名前的部分替換爲“xfire.local://”就可以了。

小結

比之於Axis,XFire在實施Web Service更加簡潔高效,因此XFire在短短的時間裏成爲了Web Service開發者的炙手可熱的框架。更可貴的是XFire對Spring提供了強大的支持,可以非常方便地在Spring中使用XFire實施Web Service。

XFire可以通過多種方式將Spring容器中的Bean導出爲Web Service,這包括使用XFireExporter導出器或JSR 181註解。JSR 181和STAX一起都將融入到JDK 6.0中,因此,JSR 181 Web Service定義方式將成爲標準的實現。

XFire爲客戶端提供了多種訪問Web Service的方式,如果可以獲取客戶端的窄接口類,則可以採用窄接口類調用Web Service。如果僅能獲取WSDL,XFire也可以採用動態反射的機制調用Web Service。XFire爲Eclipse提供了一個可以根據WSDL生成客戶端存根代碼的插件,相信XFire也將爲其它非Java語言提供類似的插 件。

技術可用性的一個很大的標準是它是否方便測試,XFire爲在Spring中測試Web Service提供了一流的支持,通過JVM模式,你能夠在不啓動Web容器的情況下測試Web Service,Web Service的測試工作變得不再象原來那樣讓人畏懼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章