XFire體系及重要API(2)

 

 

使用JSR 181註解導出Web Service

在需要導出爲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提供了各種方便的方式:在可以獲取服務端窄接口類的情況下,可以根據服務地址和窄接口類創建客戶調用程序。

如果不能獲得服務窄接口類,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所示:


圖3創建客戶端代碼存根

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

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


圖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服務的調用了。單的配置就可以了,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的代價是一次性的,性能問題就可以忽略了。

 

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