1.6 JAX-WS 开发 Web 服务
JAX-WS 是用于简化使用 Java 构造 Web 服务和 Web 服务客户机的工作的技术。该技术提供了完整的 Web 服务堆栈,可减少开发和部署 Web 服务的任务。 JAX-WS 支持 WS-I 基础总则 1.1 ,后者可确保使用 JAX-WS 堆栈开发的 Web 服务能够供采用 WS-I 基础总则标准使用任意语言开发的任意客户机使用。 JAX-WS 还包括了 JAXB ( Java Architecture for XML Binding , Java XML 绑定框架 ) , 和 SAAJ(SOAP with Attachments API for Java , SOAP 附件 API) 。
JAXB 提供了一种非常方便的方法来将 XML 模式映射到 Java 代码的表示形式,从而支持数据绑定功能。 JAXB 消除了将 SOAP 消息中的 XML 模式消息转换为 Java 代码的工作,因而不必全面了解 XML 和 SOAP 解析。 JAXB 规范定义 Java 和 XML 模式之间的绑定。 SAAJ 提供了标准的方法来处理 SOAP 消息中包含的 XML 附件。
而且, JAX-WS 提供了用于将 POJO 类转换为 Web 服务的注释库,从而加速了 Web 服务的开发工作。另外,它还指定了从采用 Web 服务描述语言( Web Services Description Language , WSDL )定义的服务到实现该服务的 Java 类之间的详细映射。采用 WSDL 定义的任意复杂类型都通过遵循 JAXB 规范定义的映射来映射为 Java 类。 JAX-WS 之前与 Java EE5 绑定。而 JAX-WS 2.0 规范是作为 JCP 的 JSR 224 开发的。
让我们首先创建一个网上书店订单处理 Web 服务,用于接受订单信息、配送信息和订购书籍并最终生成确认订单号作为响应。订单处理服务的代码如下
package tutorial.service;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
@WebService(serviceName = "OrderProcess",
portName = "OrderProcessPort",
targetNamespace = "http://tutorial/jaxws/orderprocess")
@SOAPBinding(style=SOAPBinding.Style.DOCUMENT,use=SOAPBinding.Use.LITERAL,
parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
public class OrderProcessService {
@WebMethod
public OrderBean processOrder(OrderBean orderBean) {
System.out.println("客户端调用processOrder"
+ orderBean.getCustomer());
System.out.println("所定书籍数量"+orderBean.getOrderBooks().length);
orderBean.setOrderId("2008123111");
return orderBean;
}
}
这是一个虚拟实现,将在控制台输出客户名和书籍数量,然后输出虚拟订单 2008123111 。
OrderBean 中包含订单信息,具体来说,其中包含对客户、订单项和配送地址对的内容。 OrderBean 代码如下:
package tutorial.service;
public class OrderBean {
private String customer;
private String shippingAddress;
private String[] orderBooks;
private String orderId;
public String getCustomer() {
return customer;
}
public void setCustomer(String customer) {
this.customer = customer;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(String shippingAddress) {
this.shippingAddress = shippingAddress;
}
public String[] getOrderBooks() {
return orderBooks;
}
public void setOrderBooks(String[] orderItems) {
this.orderBooks = orderItems;
}
}
开发 JAX-WS Web 服务的起点是一个使用 javax.jws.WebService Annotation 进行了注释的 Java 类。所使用的 JAX-WS 注释属于 Java 平台 Web 服务元数据( Web Services Metadata for the Java Platform )规范 (JSR-181) 的一部分。 OrderProcessService 类(带有 @javax.jws.WebService Annotation 的类)隐式地定义了服务端点接口( Service Endpoint Interface , SEI ),用于声明客户机可以对服务调用的方法。除了使用 @WebMethod 注释且 exclude 元素设置为 true 的方法外,类中定义的所有公共方法都会映射到 WSDL 操作。 @WebMethod 注释是可选的,用于对 Web 服务操作进行自定义。除了 exclude 元素外, javax.jws.WebMethod 注释还提供 operation name 和 action 元素,用于在 WSDL 文档中自定义操作名属性和 SOAP action 元素。这些属性是可选的;如果未定义,会从类名称派生缺省值。
实 现 Web 服务后,需要生成部署服务所需的所有构件,然后将 Web 服务打包为部署构件(通常为 WAR 文件),并将 WAR 文件部署到任何支持 JAX-WS 2.0 规范的兼容服务器上。通常生成的构件是提供基于服务接口将 Java 对象转换为 XML 、 WSDL 文件和 XSD 模式的功能的类。
出于测试目的, Java 6 绑定了一个轻量级 Web 服务器,可以通过调用简单的 API 调用将 Web 服务发布到该服务器上。接下来我们将了解如何使用此方法测试 Web 服务。
发布服务
发布服务首先要生成构件,运行 wsgen 工具,以生成订单处理 Web 服务的 JAX-WS 可移植构件。此工具将读取 Web SEI 类,并生成 Web 服务部署和调用所需的所有构件。 wsgen 工具生成需要发布的 Web 服务的 WSDL 文件和 XSD 模式。
先编译 OrderProcessServie 和 OrderBean ,然后把这个两个类加入到类路径中,然后执行如下命令:
wsgen tutorial.service.OrderProcessService -wsdl
wsgen 工具提供了大量的选项,例如,其中提供了 -wsdl 选项,用于生成服务的 WSDL 和 XSD 模式。运行此命令后生成了 OrderProcess.wsdl 和 OrderProcess_schema1.xsd 。为了使用 Java 6 提供的轻量级 Web 服务器,我们在 OrderProcessService 的 main 方法增加如下代码:
public static void main(String[] args) {
Endpoint.publish("http://localhost:8080/OrderProcessWeb/orderprocess",
new OrderProcessService());
}
只用运行 OrderProcessService 就将 OrderProcessService 发布成了 Web 服务。通过 Endpoint.publish() 方法,可以方便地发布和测试 JAX-WS Web 服务。 publish() 接受两个参数: Web 服务的位置和 JAX-WS Web 服务实现类。 publish() 方法在指定的 URL (本例中为本地主机,端口为 8080 )创建轻量级 Web 服务器,并将 Web 服务部署到该位置。此轻量级 Web 服务器在 Java 虚拟机( Java Virtual Machine , JVM )中运行,可通过调用 endpoint.stop() 方法以有条件的方式终止。
接下我们将了解如何从 WSDL 创建 Web 服务客户机。 JAX-WS 提供了名为 wsimport 的工具,用于从 WSDL 生成 JAX-WS 可移植构件。生成的可移植构件通常包括以下内容:
- 服务端接口 (SEI)
- 服务(需要实现的服务实现类)
- 从模式类型生成的 JAXB 生成类
- 从 wsdl:fault 映射的异常类(如果有)
客户端使用生成的构件调用 Web 服务。 Web 服务客户机并不需要处理任何 SOAP 格式(如创建或解析 SOAP 消息)。这将由 JAX-WS 运行时予以处理,此运行时将使用生成的构件代码( JAXB 生成类)。 Web 服务将处理 Java 代码( JAXB 生成类),从而减少了开发 Web 服务客户机和对 Web 服务调用操作的工作。
先使用 wsimport 工具从 OrderProcess WSDL 生成 JAX-WS 构件。然后要创建 Web 服务客户端,后者使用生成的构件代码调用订单处理 Web 服务。要生成 JAX-WS 构件,先运行 OrderProcess ,然后执行下面的命令
wsimport -keep -p tutorial.client
http://localhost:8080/OrderProcessWeb/orderprocess?wsdl
-keep 选项指示保留生成的文件, -p 选项指定需要在其中生成构件的包名称。 http://localhost:8080/OrderProcessWeb/orderprocess?wsdl 指定 WSDL 文件的位置。以下构件是从 OrderProcessService WSDL 生成的:
- JAXB 类( OrderBean ):通过读取 OrderProcessService WSDL 中定义的模式定义生成;
- RequestWrapper 和 ResponseWrapper 类封装了 SOAP 请求和应答
- 服务类 (OrderProcess) :客户机用于请求 Web 服务的类
- 服务接口 (OrderProcessService) :包含着用于服务实现接口的类
接下来了解一下如何使用上面生成的构件创建 Web 服务客户端。客户端的代码如下:
package tutorial;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import tutorial.client.*;
public class OrderClient {
final QName qName = new QName(
"http://tutorial/jaxws/orderprocess", "OrderProcess");
public static void main(String[] args) {
if (args.length != 1) {
System.out
.println("请指定订单处理Web服务的URL");
System.exit(-1);
}
URL url = getWSDLURL(args[0]);
OrderClient client = new OrderClient();
client.processOrder(url);
}
private static URL getWSDLURL(String urlStr) {
URL url = null;
try {
url = new URL(urlStr);
} catch (MalformedURLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return url;
}
public void processOrder(URL url) {
OrderProcess orderProcessingService = new OrderProcess(url, qName);
OrderBean order = populateOrder();
OrderProcessService port = orderProcessingService.getOrderProcessPort();
OrderBean orderResponse = port.processOrder(order);
System.out.println("订货单号为:" + orderResponse.getOrderId());
}
private OrderBean populateOrder() {
OrderBean order = new OrderBean();
order.setCustomer("张三");
order.getOrderBooks().add("世界是平的");
return order;
}
}
上面列出的 Web 服务客户机代码执行以下任务:
- 通过传入 OrderProcess Web 服务的 WSDL URL 和服务的 QName 创建 OrderProcess 类的实例。
- 创建 OrderBean 的实例,并使用 populateOrder() 方法填充订单信息。
- 对服务调用 getOrderProcessPort() ,以检索到服务的代理(也称为端口)。端口实现服务所定义的接口。
- 调用端口的 processOrder 方法,并同时传入在上面的第二个列表项目中创建的 OrderBean 实例。
- 从服务获得 OrderBean 响应并输出订单 ID 。
如上面的客户机代码中所示,并不会处理调用 Web 服务操作时使用的任何基于 SOAP 或 XML 的格式;相反,需要处理的是输入和输出消息的 JAXB 生成类,并使用服务接口和服务类对象。存根负责从 JAXB 注释创建 SOAP 请求,并将 SOAP 响应转换回 Java 对象。
在本节中,我们了解了如何使用 JAX-WS 技术设计和开发 Web 服务。 JAX-WS 是一个非常不错的选择,因为其中提供了完整的 Web 服务堆栈,以简化 Web 服务的开发和部署。
本节中开发的订单处理 Web 服务使用文档样式的 Web 服务,可确保服务使用者和服务提供者使用 XML 文档进行通信。 XML 文档遵循定义良好的契约,而此类契约通常都是使用 XML 模式定义创建的。 XML 模式格式指定服务使用者能够调用和遵循的业务消息的契约。文档样式的 Web 服务应该是开发企业 Web 服务的首选方法。