1.5 JAX-RPC 开发 WEB 服务简介
JAX-RPC 就是 Java API for XML-Based RPC ( Java 以 XML 为基础的远程过程调用 API )的缩写,它是在 java 平台上开发 WEB 服务重要技术规范。和其他 JAVA 技术规范一样, JAX-RPC 本身只是一组类和接口并非具体实现,所以使用 JAX-RPC 进行开发需要在一个具体实现了该规范的平台上来进行。
JAX-RPC 可以被用于开发基于 SOAP1.1 规范的 WEB 服务的服务器端和客户端。 JAX-RPC 提供一种简单的编程模式来开发基于 SOAP 协议的 WEB 服务,它屏蔽了 Web 服务的很多技术细节,开发人员在不需要了解底层协议的情况下就可以进行 Web 服务的开发。 JAX-RPC 还提供了把 WSDL 映射成 JAVA 代码以及把 JAVA 代码映射成 WSDL 的工具。
Apache AXIS 1.X 是 JAX-RPC 的开源实现,这里我们将使用 AXIS 来开发 WEB 服务。开发 web 服务有两种模式: 1 、契约优先模式; 2 程序优先模式;契约优先就是先用 WSDL 定义服务,然后在根据 WSDL 来编写程序,而程序优先模式则先编程程序然后在用工具将程序发布成 Web 服务。 AXIS 1.X 采用了第 2 种模式,先用 Java 编写代码再发布成 Web 服务。
正如前面所描述的那样,在编写作为 Web 服务发布的程序时不需要考虑 RPC 或 Web 服务。大多数 Web 服务最初并不是作为 Web 服务开发的;实际上,它们最初是一般的程序,包含一些在调用时返回值的方法。如果您熟悉这个概念,就说明已经理解了 Web 服务的本质:它们仅仅是可以通过 Web 而不是只有虚拟机访问的程序。
所以,在开始关注 RPC 语法或 Web Services Description Language ( WSDL )之前,我们需要一个可供 Web 客户机使用的类。
假设我们需要开发一个可以根据贷款年限、贷款金额、利率计算总利息的 Web 服务,现在完全不用考虑 Web 服务,就写一个简单的 java 类来计算利息就行了。
public class CalculateInterest { public float calculate(float credit,int year,float rate){ return credit*year*rate; } }
这段代码,不需要太多的解释,就是非常简单的 POJO 。接下来通过 AXIS 1.X 把这个 POJO 发布成 WEB 服务。 AXIS 提供立即发布和定义发布两种部署方式来发布 Web 服务,这里将先采用立即发布方式将 CalculateInterest 类发布成 Web 服务再来说明如何进行定制发布。采用立即发布方式只用将 CalculateInterest.java 重命名为 CalculateInterest.jws 并拷贝到部署了 AXIS 1.X 运行环境的 Web 应用中就算完成了 Web 服务的发布。有了 Web 服务但是谁来使用这个服务呢?当然是客户端,接下来我们开发 CalculateInterest 服务的客户端。按照 JAX-RPC 规范的要求客户端使用 javax.xml.rpc.Call 和 javax.xml.rpc.Service 这两个接口来实现对服务器端的调用, AXIS 1.X 提了这两个接口的实现类 org.apache.axis.client.Call 和 org.apache.axis.client.Service ,使用这两个实现类可以很轻松的构建出 Web 服务的客户端来。下面是 CalculateInterest 服务的客户端代码:
import java.io.IOException; import javax.xml.namespace.QName; import org.apache.axis.client.Call; import org.apache.axis.client.Service; import javax.xml.rpc.ServiceException; public class CalculateInterestClient { public static final String SERVICE_URL = "http://localhost:8080/AXIS_WS/CalculateInterest.jws"; public float calculate(Float credit, Integer year, Float rate) throws IOException { try { Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(SERVICE_URL); call.setOperationName(new QName("http://DefaultNamespace", "calculate")); Object result = call.invoke(new Object[] { credit, year, rate }); if (result instanceof Float && result != null) return ((Float) result).floatValue(); else return -1f; } catch (ServiceException e) { throw new IOException("Error creating service call: " + e.getMessage()); } } public static void main(String[] args) { CalculateInterestClient client = new CalculateInterestClient(); try { System.out.println(client.calculate(100000f, 10,0.0523f)); } catch (IOException e) { e.printStackTrace(); } } }
首先是创建 Service 类,然后创建 Call ,然后指定 Call 实例要访问的网络端点,设置 Web 服务具体操作的在 WSDL 中的限定名(这里不用太在意限定名的问题,后面会做进一步讲解),这个限定名包含了两个部分本地名 calculate 和名字空间 http://DefaultNamespace 然后调用 invoke 方法,就实现了对 Web 服务的调用。
从上面的代码来看和 WSLD 没有什么关系,这是因为 AXIS 1.X 采用 AXIS 默认的 RPC 服务方式访问 Web 服务,而这个 Web 服务也正好采用的 RPC 方式提供对外服务,但实际情况并不能保证所有 Web 服务都采用的这种方式,所以 AXIS 提供了可以根据 WSDL 生成客户端代码的工具,在接来介绍定制方式发布服务的部分会对这个工具进行介绍。
这个例子可以看出 AXIS 1.X 实现了对 web 服务底层的封装,不用了解 Web 服务。但是采用这种立即发布方式简单但是不够灵活,很多情况下不能适应实际的应用,例如一个 Java 有多个公有方法而很多情况下只需要把一个方法发布成 Web 服务,使用立即发布全部公有方法都会被暴露出来,这时候需要使用 AXIS 提供的定制发布。
为了方便说明定制发布,我们假设需要开发一个提供计算铁路列车票价的服务,该服务可以根据输入的历程公里数计算票价,每公里的成本票价为 0.075 元,除了成本还有 15% 的利润,这个服务有两个方法一个用于计算成本另一个用于计算加上利润后的票价,计算成本的方法不应该被发布成 Web 服务,但同一个 jvm 的类需要能访问这个方法。和前面的示例一样,这个 Web 服务的程序只是一个 POJO ,代码如下:
package railway; public class TicketSvr { public float ticketPrice(float journey){ return ticketCostPrice(journey)*(1+0.15f); } public float ticketCostPrice(float journey){ return journey*0.075f; } }
编译 TicketSvr 并把 class 文件拷贝到部署了 AXIS 的 Web 应用的 WEB-INF/classes 目录中,为了把 TicketSvr 发布成 Web 服务,使用定制方式发布服务需要编写一个名字为 deploy.wsdd 的 XML 文件,里面的内容为:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="TicketSvr" provider="java:RPC"> <parameter name="className" value="railway.TicketSvr"/> <parameter name="allowedMethods" value="ticketPrice"/> </service> </deployment>
启动部署了 AXIS 的 Web 应用,使用 AXIS 提供的命令:
java org.apache.axis.client.AdminClient -lhttp://localhost:8080/AXIS_WS/services/AdminService deploy.wsdd
这个命令会把相应的配置加入到 WEB-INF 目录下的 server-config.wsdd 文件中,用浏览器访问 http://localhost:8080/AXIS_WS/services/TicketSvr?wsdl 可以看到 TicketSvr 的 WSDL 。这里将使用 AXIS 提供的工具 WSDL2Java 生成客户端的代码。启动部署了 AXIS 的 Web 应用,把 WEB-INF/lib 目录下的所有 jar 包加入到类路径,然后执行如下命令:
java -Djava.ext.dirs=lib http://localhost:8080/AXIS_WS/services/TicketSvr?wsdl
这个命令将在 localhost/AXIS_WS/services/TicketSvr 目录下生成四个 java 文件,它们分别是:
TicketSvr.java Web 服务的接口,只有一个方法 ticketPrice ;
TicketSvrService.java 服务接口工厂的接口,定义了生成服务接口的方法;
TicketSvrServiceLocator.java TicketSvrService 接口的实现类;
TicketSvrSoapBindingStub.java 实现网络通信,具体调用 Web 服务;
只用调用这些生成的类就可以实现对 Web 服务的调用。下面就是访问 Web 服务的客户端代码。
import java.rmi.RemoteException; import javax.xml.rpc.ServiceException; import localhost.AXIS_WS.services.TicketSvr.*; public class TicketSvrClient { public static void main(String[] args) throws ServiceException, RemoteException { TicketSvrService ticketService = new TicketSvrServiceLocator(); TicketSvr ticketSvr = ticketService.getTicketSvr(); System.out.println("1100 km price is " + ticketSvr.ticketPrice(1100f)); } }
使用 JAX-RPC 可以极大的简化 Web 服务的开发,但 JAX-RPC 适用于 RPC 模式 Web 服务的开发,而在异构系统集成方面更多情况下是采用基于文档模式的 Web 服务,为了适应技术发展 JAX-WS 标准被提出来了,下面的章节我们将介绍更新的 JAX-WS 标准。