您的位置:首页 > 其它

基于xfire的webservice的实现一

2009-04-27 09:17 435 查看
要学习Flex与后台数据库的数据交互,先不用lcds,自己用webservice来写。可是webservice我也不熟悉,先来学习一下webservice.我们的工程用的是xfire来实现webservie,转载xfire的配置文章。

1. 概述

XFire

是全球众多牛人在与axis系列对比后一致投票的选择。我比较欣赏的特性有:

与Spring整合

,无须生成一堆文件,无须打包独立war,直接将应用中的Pojo导出为服务。
Aegis

--超简约的默认Java XML 绑定机制,且可以Plugin其他绑定机制。
JSR181--annotatiton驱动

的POJO WebService配置。
基于Stax的高性能框架


脱离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会被默认生成为http://www.springside.org.cn/bookstore/service/BookService

,其中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服务导出完毕,用户可在http://localhost/service/BookService?WSDL

查看自动生成的WSDL。
上半章完,关于JSR181,Client API与测试部分请看XFire 生火指南(下)

1. JSR181

JSR181式通过annotated POJO ,零配置文件的导出Web服务,是BEA倡导的,JavaEE5里的正规方式, XFire作了良好的支持。
但是,XFire关于JSR181方式的文档还不够清晰,请完整阅读本节以避免其中的数个陷阱。

1.1 参考文章

Spring, Hibernate and XFire


Webservices with Spring, XFire and jsr181


Basic Spring Web services with XFire and JSR 181


XFire JSR181参考文档



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 = "http://www.springside.org.cn")
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 = "http://www.openuri.org/2004/04/HelloWorld")
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("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL"));

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而不是http://localhost/service/BookService

来访问服务。
测试的方式分两种:
一种是纯服务器角度,不编写客户端代码,以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="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header/>
<env:Body>
<findBooksByName xmlns="http://www.springside.org.cn">
<in1>Java</in1>
</findBooksByName>
</env:Body>
</env:Envelope>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: