基於xfire的webservice的實現一

要學習Flex與後臺數據庫的數據交互,先不用lcds,自己用webservice來寫。可是webservice我也不熟悉,先來學習一下webservice.我們的工程用的是xfire來實現webservie,轉載xfire的配置文章。
 

1. 概述

   XFire 是全球衆多牛人在與axis系列對比後一致投票的選擇。我比較欣賞的特性有:
  1. 與Spring整合,無須生成一堆文件,無須打包獨立war,直接將應用中的Pojo導出爲服務。
  2. Aegis--超簡約的默認Java XML 綁定機制,且可以Plugin其他綁定機制。
  3. JSR181--annotatiton驅動的POJO WebService配置。
  4. 基於Stax的高性能框架
  5. 脫離Web服務器的單元測試能力。
   網上的文檔與例子總是不新,大家拋開所有的文檔,所有的Axis習慣,單看這份代表XFire1.2.2最簡約做法的文檔。

2. 生火指南

2.1 修改web.xml,在Web應用中增加XFire的入口

   xfire的入口,注意XFire有了自己的Servlet,不再依賴Spring MVC的Servlet,也就遠離了大家不熟悉的Spring MVC URL Mapping,與Spring達致完美的整合。
   這裏指定了路徑爲/service/* ,即WebService的URL會被默認生成爲[url]http://www.springside.org.cn/bookstore/service/BookService[/url],其中BookService默認爲2.2中的接口名。
 
<servlet>
  
<servlet-name>xfire</servlet-name>
  
<servlet-class>org.codehaus.xfire.spring.XFireSpringServlet</servlet-class>
 
</servlet>
 
<servlet-mapping>
  
<servlet-name>xfire</servlet-name>
  
<url-pattern>/service/*</url-pattern>
 
</servlet-mapping>
 

2.2 編寫窄接口,抽取POJO中要導出的服務

   從已有的BookManager.java中,抽取出一個窄接口,僅暴露需要導出爲Web Service的方法。而BookManger.java是POJO,不需要任何WebService相關代碼。
  窄接口一方面滿足了安全要求,不用整個BookManager所有方法導出爲Web Service;另一方面,XFire暫時也只支持基於接口的Proxy。
public interface BookService {
   List
<Book> findBooksByCategory(String cateoryId);
}
 

 2.3 配置Java-XML Binding

   XFire默認的Aegis Binding語法非常簡單,在SpringSide的例子裏幾乎一行配置都不用寫,是我見過最簡單的binding定義,大大優於其他以設計複雜爲終極目標的方案。
   對象的屬性、函數的參數和返回值如果爲int、String、Date等普通類型以及由普通類型組成的複雜對象都無需定義。我見到只有兩種情況需要定義:
  • 無法使用泛型定義Collection中元素的類型時--如List findBooks()。如果能寫成List<Book> findBooks()就也不需要了。
  • 需要爲屬性定義不同的名字,或者定義複雜對象裏的某些屬性不要輸出。
   XFire以約定俗成代替配置,如果萬一真的需要aegis配置,所有Service和Entity Bean的binding文件要求命名爲xxx.aegis.xml,而且要和原來的類sit together在同一目錄裏。
<mapping>
        
<!--配置findBooksByName服務的返回值,List內對象爲Book-->
        
<method name="findBooksByName">
            
<return-type componentType="org.springside.bookstore.domain.Book"/>
        
</method>
       
<!--配置Category類,忽略內嵌的products屬性不要輸出XML-->
       
<property name="products" ignore="true"/>
</mapping>
 
   其他語法詳見Aegis 參考。

2.4 配置Spring導出

    爲了節約代碼,配置一個基類,注意導出的服務不能lazy-init:
<!-- 導入XFire基本配置文件 -->
<import resource="classpath:org/codehaus/xfire/spring/xfire.xml"/>

<bean id="baseWebService" class="org.codehaus.xfire.spring.remoting.XFireExporter" lazy-init="false"
    abstract
="true">
  
<property name="serviceFactory" ref="xfire.serviceFactory"/>
  
<property name="xfire" ref="xfire"/>
 
</bean>
 
 每個Web服務的定義:parent爲基類,serviceClass property設置Web Service的接口,serviceBean property設置Web Service的實現類。
<bean id="bookService" parent="baseWebService">
  
<property name="serviceBean" ref="bookManager"/>
  
<property name="serviceClass" value="org.springside.bookstore.components.xfire.server.simple.BookService"/>
 
</bean>

Web服務導出完畢,用戶可在[url]http://localhost/service/BookService?WSDL[/url]查看自動生成的WSDL。 
上半章完,關於JSR181,Client API與測試部分請看XFire 生火指南(下)

1. JSR181

     JSR181式通過annotated POJO ,零配置文件的導出Web服務,是BEA倡導的,JavaEE5裏的正規方式, XFire作了良好的支持。
     但是,XFire關於JSR181方式的文檔還不夠清晰,請完整閱讀本節以避免其中的數個陷阱。

1.1 參考文章

1.2 ApplicationContext.xml

    因爲配置都寫在annotation,applicationContext.xml文件的內容比較固定。需要注意JSR181WebAnnotations與HandlerMapping不能lazy init.
<beans default-autowire="byName"  default-lazy-init="true">
  
<!--引入XFire的預配置文件-->
 
<import resource="classpath:org/codehaus/xfire/spring/xfire.xml"/> 

 
<!-- 獲得applicationContext中所有bean的JSR181 annotation -->
 
<bean id="webAnnotations" class="org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations"  lazy-init="false"/>


 
<!-- 定義handler mapping,將所有JSR181定義的bean導出爲web service -->
 
<bean id="jsr181HandlerMapping" class="org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping"  lazy-init="false">
  
<property name="xfire" ref="xfire"/>
  
<property name="webAnnotations" ref="webAnnotations"/>
 
</bean>
</beans>
 

1.3 Interface+Impl模式

   不同於XFire傳統模式,窄接口不是必須的,只是考慮到client如果也使用XFire時,有個接口好生成Client而已。
   如果採用Interface,Interface將擔任主要的配置工作。
   首先定義@WebService,可定義自己的NameSpace,如果不定義將採用NameSpace的默認生成算法。
   接口中的函數將默認全部導出,不需要再用@WebMethod註釋,可以如下例般進行更進一步配置:
@WebService(targetNamespace = "[url]http://www.springside.org.cn[/url]")
public interface BookService {
  @WebResult(name 
= "SearchResult")
  List
<Book> findBooksByCategory(@WebParam(name = "category", header = true)String cateoryId);
}
       Manager不是純粹的POJO,需要帶上@WebService註釋,指明InterFace。
@WebService(serviceName = "BookService"
endpointInterface 
= "org.springside.bookstore.components.xfire.server.jsr181.BookService")
public class BookManager implements BookService {...}
      陷阱一:XFire JSR181參考文檔 中在Interface中以@WebService(name="BookService")來定義ServiceName,這個做法看起來也比較合理,但實際上需要在Manager中以@WebService(serviceName ="BookService") 來定義,比較古怪。

1.4 純POJO模式

    參考文檔中的例子,需要配置@WebMethod 指定需要導出的服務
@WebService(name = "EchoService", targetNamespace = "[url]http://www.openuri.org/2004/04/HelloWorld[/url]")
        
public class Jsr181EchoService
{
    @WebMethod(operationName 
= "echoString", action = "urn:EchoString")
    @WebResult(name 
= "echoResult")
    
public String echo(@WebParam(name = "echoParam", header = true) String input)
    {
        
return input;
    }
}
 

1.5 Client注意事項

    陷阱二:和傳統模式的client有一點最大的區別,第3個參數需要是實際的Manager類,而不是接口類:
Service serviceModel = new AnnotationServiceFactory().create(BookManager.class);

2. Client

XFire的Client並不算強項,一共有三種模式:

2.1 Client開發者擁有Web服務端的class

  Client與Server是同一個開發團隊也好,Server端團隊以jar形式提供開發包也好,反正如果能拿到服務端的接口Class和Entity類及aegis 配置文件的話。
  傳統模式:
Service serviceModel = new ObjectServiceFactory().create(BookService.class);
BookService service 
= (BookService) new XFireProxyFactory().create(serviceModel, serviceURL);
service.findBooksByCategory(cateoryId);
  JSR181模式,注意這裏Server端開發組需要向Client提供BookService的實現類BookManager,而不止於接口類,有點危險: 
Service serviceModel = new AnnotationServiceFactory().create(BookManager.class);
BookService 
= (BookService) new XFireProxyFactory().create(serviceModel, serviceURL);
service.findBooksByCategory(cateoryId);
 
  SpringSide 用泛型封裝了一個XFireClientFactory,調用代碼如下:
BookService service = XFireClientFactory.getClient(serviceURL, BookService.class);
BookService service 
= XFireClientFactory.getJSR181Client(serviceURL, BookService.class, BookManager.class);

2.2 動態模式

    動態模式不需要服務端的class,不過性能和複雜對象映射等估計做得不會太好。
Client client = new Client(new URL("[url]http://www.webservicex.net/CurrencyConvertor.asmx?WSDL[/url]"));

Object[] results 
= client.invoke("ConversionRate"new Object[] {"BRL""UGX"});

2.3 根據WSDL生成Client Stub

    這纔是Web Service Client的王道,可以訪問任意編寫下的Web Service,將在下一個版本中演示。

3. 測試

XFiire很重要的一個特性是提供了無須啓動Web容器也能進行單元測試的能力。
原理就是利用XFire的JVM模式,以xfire.local://BookService channel而不是[url]http://localhost/service/BookService[/url]來訪問服務。
測試的方式分兩種:
一種是純服務器角度,不編寫客戶端代碼,以SOAP XML形式發送請求,返回的也是SOAP XML字串,直接對XML進行測試。
一種是編寫2.1 中Client代碼來進行測試。
前一種的測試的隔離度較高,而後一種比較簡便。

3.1 測試基類 

無論那種方式,都使用Xfire的AbstractXFireSpringTest基類,實現createContext()回調函數。
protected ApplicationContext createContext() {
      
return ClassPathXmlApplicationContext(new String[]{"classpath*:applicationContext*.xml"});
 }
另外測試基類還要完成一個很重要的工作就是要解決Hibernate的LazyLoad問題,做到OpenSession In Test。因此,SpringSide專門封裝了一個XFireTestCase的基類。

3.2 用Client代碼直接測試

     下文直接用client代碼調用findBooksByCategory方法,得到返回值後進行各種Assert判斷。
     注意和普通client code的兩處區別:servericeURL換成local,factory須加入getXFire()作參數。
Service serviceModel = new ObjectServiceFactory().create(BookService.class);
  XFireProxyFactory factory 
= new XFireProxyFactory(getXFire());
  BookService service 
= (BookService) factory.create(serviceModel, "xfire.local://BookService");
  List list 
= service.findBooksByCategory("0");
  assertNotNull(list);
  ...
 

3.3 純服務端測試

   編寫一段SOAP XML,以任意命名保存,下文以"Java"作參數,調用findBooksByName方法。
<env:Envelope xmlns:env="[url]http://schemas.xmlsoap.org/soap/envelope/[/url]">
    
<env:Header/>
    
<env:Body>
        
<findBooksByName xmlns="[url]http://www.springside.org.cn[/url]">
            
<in1>Java</in1>
        
</findBooksByName>
    
</env:Body>
</env:Envelope>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章