1.引言
1.1. 目的
Java 开发WebService 的开发有多种,而且没有特别是没有一个统一的标准实现,XFire,Axis2,CXF等;本文档主要是讲使用XFire来实习WebService的开发。
1、为了避免在以后编写WebService时,研发人员的重复工作。
2、为了避免不必要的矛盾和冲突,减少研发、维护成本。
1.2. 范围
适合使用Web Services的情况
1、跨越防火墙;
2、应用程序集成;
3、B2B集成;
4、软件重用
不适合使用Web服务的情况
1、单机应用程序;
2、局域网上的同构应用程序
1.3. 术语与缩写
序号 |
术语 |
说明 |
1 |
SOAP |
简单对象访问协议(SOAP)提供了标准的RPC方法来调用Web service |
2 |
WSDL |
Web service描述语言(WSDL)就是这样一个基于XML(标准通用标记语言下的一个子集)的语言 |
3 |
XML |
可扩展的标记语言(标准通用标记语言下的一个子集)是Web service平台中表示数据的基本格式 |
4 |
|
|
5 |
|
|
2.实现功能特点
A. 基于J2EE 平台的Web Service 服务
B. 开发方便,配置简单
l 设计接口
l 实现服务
l 配置暴露接口
l XFire 将自动生成对应的wsdl
l 支持高级详细配置
C. 与Spring 无缝集成
3.运行环境
JDK 1.5+
Tomcat 5.0+ / WebLogic 8.1 (需要特殊配置,见附录)未测试其他环境
其他包依赖参看 http://xfire.codehaus.org/Dependency+Guide
4.开发平台
Eclipse
Tomcat 6.0
XFire 1.2.6 - http://xfire.codehaus.org/Home
Maven3.0
5.开发步骤
Java 开发WebService 的开发有多种,而且没有特别是没有一个统一的标准实现,XFire,Axis2,CXF等。下面我们选用 XFire来开发WebService;XFire与其他WebService框架的不同,它最大的不同之处在于它需要一个接口,而且如果需要用XFire来调用相应的WebService必须知道接口的定义,这感觉这里有点限制但也符合。但这点也符合Jave的面向对象的特点。而且XFire调用WebService,那是相当的方便,就跟调用本地方法一样
我们先虚拟一个场景:
服务端:
1、提供获取订单信息的方法;
2、提供添加订单信息的方法;
客户端:
1、根据订单ID获取服务端的订单信息;
2、向服务段添加一个订单信息;
以下是两种模式的实现方式:
1.1. XFire简单模式
简单模式服务器端的4个步骤:
1. 提供一个接口,接口里面有两个方法,一个是获取订单信息的方法,一个是添加订单信息的方法。
2. 实现上面的接口。
3. 配置XFire的services.xml
4. 添加XFire的jar包依赖,并在Web.XML 新增WebService 的请求拦截
下面我们开始编写代码:
1.1.1. 订单实体
代码:
package com.topinfo.xfire.bean; import java.io.Serializable; /** * @Description: 订单实体 * @Author:杨攀 * @Since:2014年3月24日下午3:09:45 */ public class Order implements Serializable { /** *@Fields serialVersionUID : 序列化 */ private static final long serialVersionUID = 3089238328535163124L; /** * @Fields orderId : 订单号 */ private String orderId; /** * @Fields orderName : 订单名称 */ private String orderName; public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } @Override public String toString() { return "订单号为:" + orderId + ",订单名称:" + orderName; } }
注意:我们这里的Order类实现了Serializable接口,Java在互联网上传递对象,需要序列化。
1.1.1. 服务器提供接口
接口代码:
package com.topinfo.xfire.service; import java.util.List; import com.topinfo.xfire.bean.Order; /** *@Description:订单接口服务器 *@Author:杨攀 *@Since:2014年3月24日下午3:06:59 */ public interface OrderService { /** *@Description: 根据订单号获取订单信息 *@Author: 杨攀 *@Since: 2014年3月24日下午3:08:07 *@param orderId 订单号 *@return */ public Order queryOrder(String orderId); /** *@Description: 保存订单信息 *@Author: 杨攀 *@Since: 2014年3月24日下午3:09:04 *@param order *@return */ public String saveOrder(Order order); /** *@Description: 返回List *@Author: 杨攀 *@Since: 2014年3月25日上午10:14:06 *@param num *@return */ public List<Order> queryOrderList(int num); }
1.1.1. 服务器接口的实现
实现类代码:
package com.topinfo.xfire.serviceImpl; import java.util.ArrayList; import java.util.List; import com.topinfo.xfire.bean.Order; import com.topinfo.xfire.service.OrderService; /** *@Description:订单接口服务器的实现 *@Author:杨攀 *@Since:2014年3月24日下午3:18:13 */ public class OrderServiceImpl implements OrderService { public Order queryOrder(String orderId) { Order bean = null; if(null != orderId && !"".equals(orderId)){ bean = new Order(); bean.setOrderId(orderId); bean.setOrderName("ZJTX-"+orderId); } return bean; } public String saveOrder(Order order) { return order.toString(); } public List<Order> queryOrderList(int num) { List<Order> list = new ArrayList<Order>(); for (int i = 0; i < num; i++) { Order order = new Order(); order.setOrderId("ZJTX-"+i); order.setOrderName("订单名称"+i); list.add(order); } return list; } }
1.1.1. 创建XFire的services.xml文件
在src目录下新建一个META-INF文件夹,再在它下面新建一个xfire的文件夹,里面新建一个services.xml的文件。我们这么建的目的是想让eclipse帮我们直接部署到tomcat容器中。其实我们只要保证在部署后的\WEB-INF\classes下面有META-INF\xfire\services.xml目录格式就行。
注意:这个文件所在文件夹层次是固定的,不可以修改。
代码如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xfire.codehaus.org/config/1.0"> <service> <!-- webservice 名称,调用时需要指定这个 --> <name>OrderService</name> <!-- 这个一般是自己公司的网址,意义不大 --> <namespace>http://com.topinfp/OrderService</namespace> <!-- 接口类 --> <serviceClass>com.topinfo.xfire.service.OrderService</serviceClass> <!-- 实现类 --> <implementationClass>com.topinfo.xfire.serviceImpl.OrderServiceImpl</implementationClass> <!--注册监听处理器 <inHandlers> <handler handlerClass="com.topinfo.xfire.listener.HandlerMappingListener"></handler> </inHandlers> --> </service> </beans>
1.1.1. 添加Jar的依赖并添加请求拦截
添加Jar包的依赖
<!-- xfire --> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>org.codehaus.xfire</groupId> <artifactId>xfire-aegis</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>org.codehaus.xfire</groupId> <artifactId>xfire-spring</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>xalan</groupId> <artifactId>xalan</artifactId> <version>2.7.0</version> </dependency>
在web.xml中新增代码:
<servlet> <servlet-name>XFireServlet</servlet-name> <servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>XFireServlet</servlet-name> <url-pattern>/webservice/*</url-pattern> </servlet-mapping>
1.1.1. 订单服务端的目录结构
结构下:
1.1.1. 启动服务器端
浏览器中输入:
http://localhost:8080/OrderService/webservice/OrderService?wsdl
完成后,我们就可以让别人调用我们的WebService了,下面我模拟客户端来调用WebService。
注意:目前接口中有返回 List的 方法去掉才能正常启动的啊,原因看后面的扩展
1.1.1. 客户端测试
客户端测试需要把服务器端的接口和实体打成jar包给客户端引用,或者根据wsdl 生产客户端,网上有很多小工具或者通过 eclipse 生产也许,手写也行,代码如下:
// 这里是创建一个service,需要传入一个接口类,因为我们后面必须调用相应的接口方法 Service srcModel = new ObjectServiceFactory().create(OrderService.class); // 代理工厂,这里是为了后面创建相应的接口类 // XFireProxyFactory factory = new XFireProxyFactory(XFireFactory.newInstance().getXFire()); XFireProxyFactory factory = new XFireProxyFactory(); String readerServiceUrl = "http://localhost:8080/OrderService/webservice/OrderService"; try { // 利用工厂返回相应的接口类 OrderService orderService = (OrderService) factory.create(srcModel, readerServiceUrl); List<Order> orderList = orderService.queryOrderList(5); for (int i = 0; i < orderList.size(); i++) { System.out.println(orderList.get(i)); } } catch (MalformedURLException e) { e.printStackTrace(); }
1.1.1. 总结
当我们需要提供一个WebService接口的时候,我们需要4步:
1、提供接口和实现类
2、创建XFire的services.xml 文件
3、添加Jar的依赖
4、在web.xml中WebService的请求Maping
这样 WebService 就完成了!
1.1.2. 扩展
1、当返回对象的复杂对象时,处理方式:
如返回List
因为我们用到了List等集合类型,所以需要定义Mapping关系,文件名为:接口名称.aegis.xml,存放到接口的同一包下。代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!-- 该文件用来描述IUsersService接口中getUsers()方法返回值的类型 该文件必须与IUsersService位于同一目录中,且该文件遵循如下命名规则 webservice接口名.aegis.xml 如本文件IUsersService.aegis.xml --> <mappings> <!-- 映射方法返回值类型 --> <mapping> <method name="queryOrder"> <return-type componentType="com.topinfo.xfire.bean.Order" /> </method> <method name="queryOrderList"> <return-type componentType="com.topinfo.xfire.bean.Order" /> </method> </mapping> </mappings>
2、请求的校验(基于Xfire SOAP Header的WebService安全验证)
共以下 4 步骤:
1、服务器段添加安全验证的监听类
package com.topinfo.xfire.listener; import org.codehaus.xfire.MessageContext; import org.codehaus.xfire.handler.AbstractHandler; import org.jdom.Element; /** * @Description: 监听处理器 * @Author:杨攀 * @Since:2014年3月25日上午11:19:55 */ public class HandlerMappingListener extends AbstractHandler { public void invoke(MessageContext context) throws Exception { // 为SOAP Header构造验证信息 if (context.getInMessage().getHeader() == null) { throw new org.codehaus.xfire.fault.XFireFault("请求必须包含验证信息", org.codehaus.xfire.fault.XFireFault.SENDER); } Element token = context.getInMessage().getHeader().getChild("AuthenticationToken"); if (token == null) { throw new org.codehaus.xfire.fault.XFireFault("请求必须包含身份验证信息", org.codehaus.xfire.fault.XFireFault.SENDER); } String username = token.getChild("Username").getValue(); String password = token.getChild("Password").getValue(); try { // 进行身份验证 ,只有admin/admin 的用户为授权用户 if ("admin".equals(username) && "admin".equals(password)) { System.out.println("身份验证通过"); } else { throw new Exception(); } } catch (Exception e) { throw new org.codehaus.xfire.fault.XFireFault("非法的用户名和密码", org.codehaus.xfire.fault.XFireFault.SENDER); } } }
1、把监听类注册到XFire的services.xml 文件中
<!--注册监听处理器--> <inHandlers> <handler handlerClass="com.topinfo.xfire.listener.HandlerMappingListener"></handler> </inHandlers>
1、 2、客户端构造授权信息
package com.topinfo.xfire.listener; import org.codehaus.xfire.MessageContext; import org.codehaus.xfire.handler.AbstractHandler; import org.jdom.Element; /** *@Description: 监听处理器 *@Author:杨攀 *@Since:2014年3月25日上午11:19:55 */ public class ClientHandler extends AbstractHandler { private String username = null; private String password = null; public ClientHandler() { } public ClientHandler(String username, String password) { this.username = username; this.password = password; } public void invoke(MessageContext context) throws Exception { // 为SOAP Header构造验证信息 Element el = new Element("header"); context.getOutMessage().setHeader(el); //添加一个 AuthenticationToken 的元素 Element auth = new Element("AuthenticationToken"); Element username_el = new Element("Username"); username_el.addContent(username); Element password_el = new Element("Password"); password_el.addContent(password); auth.addContent(username_el); auth.addContent(password_el); el.addContent(auth); } }
1、发送授权信息
package com.topinfo.xfire.client; import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.util.List; import org.codehaus.xfire.client.Client; import org.codehaus.xfire.client.XFireProxy; import org.codehaus.xfire.client.XFireProxyFactory; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.service.binding.ObjectServiceFactory; import com.topinfo.xfire.bean.Order; import com.topinfo.xfire.listener.ClientHandler; import com.topinfo.xfire.service.OrderService; public class ListenerTest { public static void main(String[] args) { // 这里是创建一个service,需要传入一个接口类,因为我们后面必须调用相应的接口方法 Service srcModel = new ObjectServiceFactory().create(OrderService.class); // 代理工厂,这里是为了后面创建相应的接口类 // XFireProxyFactory factory = new XFireProxyFactory(XFireFactory.newInstance().getXFire()); XFireProxyFactory factory = new XFireProxyFactory(); String readerServiceUrl = "http://localhost:8080/OrderService/webservice/OrderService"; try { // 利用工厂返回相应的接口类 OrderService orderService = (OrderService) factory.create(srcModel,readerServiceUrl); //在报头加入信息,供安全校验 XFireProxy proxy = (XFireProxy) Proxy.getInvocationHandler(orderService); Client client = proxy.getClient(); // 发送授权信息 client.addOutHandler(new ClientHandler("admin","admin1")); List<Order> orderList = orderService.queryOrderList(5); for (int i = 0; i < orderList.size(); i++) { System.out.println(orderList.get(i)); } } catch (MalformedURLException e) { e.printStackTrace(); } } }
OK 校验添加完成!
1.1. XFire与Spring无缝集成模式
步骤:
1、 添加jar依赖
2、 修改web.xml
3、 修改applicationContext.xml
4、 编写接口和实现
与spring 整合的时候,就不需要在配置 XFire 的service.xml文件,其他地方和原来的简单模式一样
1.1.1. 添加依赖
添加Jar依赖:注意包冲突的问题
<!-- xfire 依赖包 --> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>org.codehaus.xfire</groupId> <artifactId>xfire-aegis</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>org.codehaus.xfire</groupId> <artifactId>xfire-spring</artifactId> <version>1.2.6</version> <exclusions> <exclusion> <artifactId>spring</artifactId> <groupId>org.springframework</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>xalan</groupId> <artifactId>xalan</artifactId> <version>2.7.0</version> </dependency>
1.1.1. 修改Web.XML 文件
1、添加 xfire的配置文件,
<context-param> <param-name>contextConfigLocation</param-name> <!-- 引入 classpath:org/codehaus/xfire/spring/xfire.xml --> <param-value>classpath:org/codehaus/xfire/spring/xfire.xml,classpath:applicationContext.xml</param-value> </context-param>
2、添加WebService 的Mapping 配置
<!-- begin XFire 配置 --> <servlet> <servlet-name>XFireServlet</servlet-name> <!-- 不整合spring 时使用org.codehaus.xfire.transport.http.XFireConfigurableServlet --> <servlet-class>org.codehaus.xfire.spring.XFireSpringServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>XFireServlet</servlet-name> <url-pattern>/webservice/*</url-pattern> </servlet-mapping> <!-- end XFire 配置 -->
1.1.1. 修改applicationContext.xml
<!-- =================== 通知公告 =================== --> <bean id="iTSMEStandardService" class="com.topinfo.xfire.webserviceImpl.ITSMEStandardServiceImpl"> <property name="slNoticeServiceImpl" ref="slNoticeServiceImpl"></property> </bean> <bean name="WebService" class="org.codehaus.xfire.spring.ServiceBean"> <!-- 业务接口实现类 --> <property name="serviceBean" ref="iTSMEStandardService"/> <!-- 业务接口 --> <property name="serviceClass" value="com.topinfo.xfire.webservice.ITSMEStandardService"/> <property name="inHandlers"> <list> <ref bean="addressingHandler"/> <ref bean="handlerMappingListener"/><!--普通的用户名密码的方式进行WebService的验证--> </list> </property> </bean> <bean id="handlerMappingListener" class="com.topinfo.xfire.webservice.listener.HandlerMappingListener"/> <bean id="addressingHandler" class="org.codehaus.xfire.addressing.AddressingInHandler"/>
1.1.1. 编写接口和实现类
这里就是普通的接口和实现类,省略:
这样。。webservice 就完美搞定了,,篇幅有点大。可能有看不清楚的地方,大伙可以下载我的word吧!