XFire 架构的 WebService 开发
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吧!