使用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的代价是一次性的,性能问题就可以忽略了。
转载于:https://blog.51cto.com/haidao/516743