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响应所经历的所有阶段:
图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所示:
阅读这个WSDL文档,我们可以知道BbtForum的getRefinedTopicCount已经被成功地发布为Web Service了。只要拿到这个WSDL就可以开发相应的客户端调用程序了。
如果输入、输出的对象类型仅包括基本类型的属性,仅需要在业务类和窄接口中分别使用@WebService注解进行简单的配置就可以了,XFire将根据默认约定导出Web Service。
窄接口仅需要定义一个@WebService注解,并指定SOAP的命名空间就可以了:
package com.baobaotao.xfire.server;
@WebService(targetNamespace = "http://www.baobaotao.com")①指定SOAP的命名空间
public interface BbtForumService {
int getRefinedTopicCount(int lastDay);
XFire应用JSR 181比较怪的一点是,除了需要在窄接口中提供注解外,在实现业务类中也需要提供相应的注解:
package com.baobaotao.service;
import com.baobaotao.xfire.server.BbtForumService;
@WebService(serviceName = "BbtForumService",①指定导出的Web Service名称
endpointInterface = "com.baobaotao.xfire.server.BbtForumService")②对应的窄接口
public class BbtForum implements BbtForumService{
如果碰到以下应用场景:输入、输出对象是复杂的对象(如未使用泛型的集合类),当返回类型是一个对象但不希望输出所有的结果,或者不希望使用默认的属性名。
代码清单3 applicationContext.xml:使用JSR 181导出Web Service
<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 id="bbtForum" class="com.baobaotao.service.BbtForum" />
XFire为访问服务端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 serviceModel = new ObjectServiceFactory().create(BbtForumService.class);
String serviceURL = "http://localhost:8080/baobaotao/service/BbtForumService";
BbtForumService service = null;
service = (BbtForumService) new XFireProxyFactory().
create(serviceModel, serviceURL);
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启动的情况下,运行以上的客户端代码,将可以获得正确的输出。
package com.baobaotao.xfire.client;
import org.codehaus.xfire.client.Client;
public int getRefinedTopicCount() {
String wsdl = "com/baobaotao/xfire/client/BbtForumService.wsdl";①对应的WSDL文件
Resource resource = new ClassPathResource(wsdl);
Client client = new Client(resource.getInputStream(), null);②根据WSDL创建客户实例
Object[] results = client.invoke("getRefinedTopicCount",new Object[]);
throw new RuntimeException(e);
public static void main(String[] args) {
OnlyWsdlClient client = new OnlyWsdlClient();
System.out.println("topic count is:" + client.getRefinedTopicCount());
可能会有读者认为这种完全根据WSDL创建客户端程序的方式会带来低劣的运行性能,笔者通过测试发现,确实会造成一定的性能降低,但也不象想象中那样低效。
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/的网站;
使用插件创建客户端代码存根
指定WSDL文件的位置,存根代码的输出地址及对应的类包,点击Finish。
下面,我们利用XFire生成的BbtForumServiceClient对服务端的Web Service进行调用:
package com.baobaotao.xfire.client;
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);
- 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节点特定路径下未包含元素。
代码清单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">
xmlns="http://server.xfire.baobaotao.com">
<in0 xmlns="http://server.xfire.baobaotao.com">20</in0>
<?xml version="1.0" encoding="UTF-8"?>
<getRefinedTopicCountResponse xmlns="http://www.baobaotao.com">
</getRefinedTopicCountResponse>
下面,我们着手编写测试BbtForumService Web Service的测试类,以验证实际SOAP响应报文是否和代码清单7中的一样:
package com.baobaotao.xfire.server;
import org.codehaus.xfire.spring.AbstractXFireSpringTest;
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(
public void testUsingSoapRequest() throws Throwable {
②通过SOAP请求报文访问BbtForumService的Web Service,对返回
Document response = invokeService("BbtForumService",
"/com/baobaotao/xfire/server/request_soap.xml");
assertNoFault(response);③确认不包含错误
addNamespace("k", "http://www.baobaotao.com");
assertValid("//soap:Body/k:getRefinedTopicCountResponse", response);⑤
assertXPathEquals("//k:getRefinedTopicCountResponse/k:out/text()", "32",response);⑥
printNode(response);⑦打印响应报文,以便肉眼查看
理解以上测试代码中几个断言方法的关键在于理解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命名空间中的元素。
能够不启动Web服务器的情况下通过客户端程序测试Web Service的功能,这一崭新的测试方法对于开发人员来说一定深具吸引力。因为,这意味着你可以完全在IDE环境中运行测试,不需要外部环境的支持。
代码清单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(
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);
Spring与XFire相关推荐
- xfire调用php,spring 组合xfire使用的详细配置
spring 结合xfire使用的详细配置 这几天在弄WebService的东西,是使用XFire+spring的.走了不少的弯路,差点想放弃了.为了日后方便,先记下这几天的收获. 刚开始的时候,不知 ...
- spring整合xfire
2019独角兽企业重金招聘Python工程师标准>>> 整合所需jar包 转载于:https://my.oschina.net/u/1418903/blog/472475
- Maven 的41种骨架功能介绍
转自:https://www.cnblogs.com/iusmile/archive/2012/11/14/2770118.html Maven 的41种骨架: ...>mvn archetyp ...
- Maven如何用Eclipse创建一个Maven项目【笔记自用】
1.Maven的安装与设置环境变量 想要创建一个Maven项目需要先安装Maven,并且设置好环境变量 去下载Maven 设置环境变量 新建变量MAVEN_HOME,值为Maven的目录X:\XXX\ ...
- struts2 集成webservice 的方法
由于项目需求的需要,要在原来用Struts2的框架之上集成webservice,因为之前单单做webservice的时候没有多大问题,使用 Spring 和 Xfire就可以轻松地发布服务,但是,当和 ...
- Maven(6)--archetype
Archetype是Maven工程的模板工具包.一个Archetype定义了要做的相同类型事情的初始样式或模型.这个名称给我们提供来了一个一致的生成Maven工程的方式.Archetype会帮助作者给 ...
- idea教程--Maven 骨架介绍
简单的说,Archetype是Maven工程的模板工具包.一个Archetype定义了要做的相同类型事情的初始样式或模型.这个名称给我们提供来了一个一致的生成Maven工程的方式.Archetype会 ...
- 程序员找工作必看面试题1
1 / 100 目录 1. 说出一些数据库优化方面的经验?(重点).......................................................8 2. 你有优化 SQ ...
- Maven创建时archetype的选择说明
新建Maven project项目时,需要选择archetype. 那么,什么是archetype? archetype的意思就是模板原型的意思,原型是一个Maven项目模板工具包.一个原型被定义为从 ...
最新文章
- 轻谈 return i++
- jQuery综合案例——打地鼠(教学版本·附源码)
- Mysql时间格式转换
- java判断实例_java判断类和实例的关系
- python自动化测试常见面试题二_思考|自动化测试面试题第二波
- 前端构建工具 Gulp.js 上手实例
- osgEarth 加载矢量shp数据
- 同一局域网内怎样获取新来美眉QQ号码!((*^__^*) 嘻嘻……)
- colmak键盘_萌神进化 IKBC 新POKER2机械键盘体验
- 什么软件可以让手机使用免费WiFi上网
- Pytorch transform
- tongweb7启动参数配置配置个人理解
- 保护域及安全域的概念:受保护的资源所在的区域
- 详解 n 维向量、n 维数组 和 矩阵的维度(转载)
- 快速申请开通公众号门店小程序-微信小程序开发-视频教程7
- 利用VB批量增加行高,解决Excel打印字体显示不全问题
- 【读书札记】《北大授课》中华文化四十七讲 余秋雨
- Ubuntu (20.4,最新版)安装及简单入门操作
- 软件测试面试必看宝典(含面试题和笔试题)
- cdq分治和整体二分