1、前言 在 Internet 网络覆盖全球的今天,网络通信已经是当今软件开发过程中离不开的话题。在常用的Windows、Liunx、Unix 系统当中,大部分的网络数据传输都是使用 TCP/IP、UDP/IP 作为底层传输协议的,而 HTTP 协议就是基于 TCP/IP 协议而运行的超文本传送协议。 在 JAVA 高级开发语言中,陆续出现 RMI、CORBA、JAX-RPC、JAX-WS、Axis、XFire、HTTP Invoker、Hessian、Burlap、JMX 等远程通信架构去实现系统之间数据传送。在 “远程通信技术” 的一系列文章中,本人将对上述复杂的 JAVA 远程通信技术作
2、出归纳。 首先,在本篇文章中先对有着10多年历史的 Axis 进行介绍。 目录 一、Axis 简介 二、Axis 1.x 实例 三、Web 服务会话管理 四、自定义Handler 五、新一代 SOAP 引擎 Axis 2.x 六、AXIOM 对象模型 七、Module 模块独立化处理方式 八、异步调用Web服务 一、Axis 简介 1.1 Web 服务的起源 Web 服务是现今实现网络服务概念的趋势,它把基础架构建立于标准化的XML语言之上,能够使用一种与平台无关的方式对数据进行编码,其中 SOAP 与 WSDL 都遵从此标准化的 XML
3、编码规则。 SOAP (Simple Object Access Protocol,简单对象访问协议)是一种轻量的、简单的、基于 XML 的协议,用于描述在服务过程中服务器端与客户端之间所交换的消息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议( HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。 WSDL (Web Service Definition Language,Web服务描述语言)是一种基于 XML的协议,用于定义服务端与客户端之间的契约,描述Web服务的公共接口,列出 Web服务进行交互时需要绑定的协议和信息格式。 W
4、eb 服务采用 WSDL 语言描述该服务支持的操作和信息,运行时再将实际的数据以 SOAP 方式在服务端与客户端进行信息传递。 由于软件开发平台众多,当中存在不同的开发风格,当服务器端与客户端使用不同的开发工具时,数据转换成为复杂且关键的问题。而 SOAP 与 WSDL 的主要特性之一在于它们都是可扩展的, 且与开发平台无关。为了建立统一的 XML 协议, 微软、IBM、Sun、Oracle、BEA 等多家软件开发商联合起来,组成了一个名为WS-I(Web Service Interoperability)组织,由该组织制定 WS-ReliableMessage、WS-Discovery、W
5、S-Federation、WS-Coordination、WS-AtomicTransaction、WS-BusinessActivity 、WS-Enumeration、WS-Eventing 、WS-Management 等一系列用于数据交换的规范。 1.2 JAX-RPC 、JAX-WS 简介 JAX-RPC ( Java API for XML-based RPC ) 是 Java 库中基于 XML 远程服务的一组标准 API,它通过 WSDL 方式对所提供的服务进行描述,并以 RPC 的风格把 SOAP 信息进行公开,是 Java 库中最早对 Web 服务提供支持的一组API。
6、 JAX-RPC 1.0 从其名称可以看出,最初的目的只是为了支持使用(Remote Procedure Call,RPC) 的 XML 远程过程调用操作,它以 BP 1.0 (WS-I’s Basic Profile 1.0)为基础,依赖于SAAJ 1.2(SOAP with Attachments API for Java)为规范,虽然支持 SOAP 协议,但对 Web 服务功能有一定的局限性。于是在 2003 年底,开发团队对 JAX-RPC 1.0 进行大幅修订,由 Sun 公司组织了一个专家组开始进行 JAX-RPC 2.0 规范的开发。 JAX-RPC 2.0 是基于 JAVA
7、5 而开发的,它依赖于 Annotation 等新特性,在JAX-RPC 的基础上提供还增加了如异步回调,面向消息等新增技术。JAX-RPC 2.0 以 BP 1.1(WS-I’s Basic Profile 1.1 ) 为基础,依赖于SAAJ 1.3(SOAP with Attachments API for Java)为规范,能使用 SOAP 1.1、SOAP 1.2 进行信息公开。它是 JAX-RPC 1.1 架构发展的成果,在开发完成后,JAX-RPC 2.0 被正式改名成为 JAX-WS ( Java API for XML-Web Services ) 。 1.3 Axis
8、 概述 Axis 全称 Apache EXtensible Interaction System ( 阿帕奇可扩展交互系统 ) , 它是一个 SOAP 引擎,提供创建 Web 服务的基本框架。Axis 1.x 是基于 JAX-RPC 而实现一个工具包,它可以使用 HTTP、JMS、SMTP 等多种传输方式支持 SOAP 。 Axis 2.x 是新一代的 Axis 引擎,它支持 JAX-WS、JAX-PRC 等 API,并且在Axis 1.x 的基础上增加了灵活数据绑定、异步调用等新增功能,可使用 SOAP 1.1 、SOAP 1.2 协议。在服务请求上,Axis 2.x 支持三种请求-响
9、应模式:In-Only、Robust-In和In-Out,也可支持使用 REST 风格的开发方式。 基本的 Axis Web 服务由四部分组成:Axis Servlet 、Axis 部署描述、 远程服务接口、服务实现类。 Axis Servlet 是 Axis 的核心,它负责 WSDL 基础服务信息的公开,并把 SOAP 请求转化为 Java 方法的调用,最后把返回值转化为 SOAP 。Axis Servlet 隐藏了构建 Web 服务的大量代码, 使用开发人员不用直接与 SOAP 打交道便可轻松完成 Web 服务的开发。 Axis 部署描述是一个XML文档,它用于管理 Web 服务的发
10、布,决定哪些服务类需要通过 SOAP 对外公开 。 远程服务接口并非必要的,但在很多的 Web 服务开发过程中都会使用远程服务接口用于对外暴露服务类的方法,在服务器端通过服务实现类去继承实现服务接口。 由于 Axis 1.x 与 Axis 2.x 有各自的特色,下面将分开来介绍。 回到目录 二、Axis 1.x 实例 2.1 Axis 1.x 的下载与安装 Axis 1.x 可于官网 http://axis.apache.org/axis/ 下载,完成下载后建立 Web Project 作为测试项目 ,把 lib 文件夹下的 jar 文件拷贝,引入到测试项目当中。 在 web.x
11、ml 文件下加入 AxisServlet 配置后,系统就会对以后缀为 *.jws 及路径为 /services/* 的请求进行监听,遇到此类请求时将把信息交由 org.apache.axis.transport.http.AxisServlet 进行处理。
1 12、ener-class>
5 13、 服务管理 -->
14 14、21
22
23 15、 16、pattern>/servlet/AxisServlet
38 17、
18、 配置,可以便于对服务运行时所传输的SOAP信息进行监听,在服务的requestFlow或responseFlow 中加入 SOAPMonitorHandler , 系统就可显示服务请求和回发时的 SOAP 信息。
1 20、ame="serviceName" value="SOAPMonitorService"/>
8 21、ameter name="className" value="org.apache.axis.monitor.SOAPMonitorService"/>
13
23、 2.2 调用服务的三种方式 下面从最简单的 HelloWorld 开始,介绍 Axis 的使用方法。首先在 WEB-INF 文件夹下建立 server-config.wsdd 文件,在 Axis 1.x 当中,此文件正是用于管理服务发布的默认配置文件。首先 service 用于定义对外暴露的服务,其中 name 属性用于定义服务的名称。像下面例子,当 name 为 PersonService 时,对外暴露的服务路径则对应为 http://localhost:8080/axis.server/services/PersonService 。 而
24、parameter 用于定义服务的相关属性,className 表示此服务的实现类,而 allowedMethods 表示所公开的服务方法,“*" 则默认为公开此类中的所有 public 公共方法。而 scope 则是用于定义服务对象生成的方式,它包括三个选项:request、session、application 。 request 是默认选择,表示为每个请求生成一个服务对象; session 表示对同一个客户代理对象所发送的请求使用同一个服务对象,并把服务信息放在同一个上下文当中。当使用有状态服务时,使用此 session 更为合适,在下节将再作进一步介绍; applicatio
25、n 类似于使用单体模式,表示所示的请求均使用同一个服务对象,当使用无状态服务时使用 application 能有效提高运行效率。
最后在 transport 中定义一个 requestFlow 处理类 org.apache.axis.handlers.http.URLMapper,表示在系统接受到 http 请求时,将会调用 URLMapper 类来处理路径映射等问题。
1 28、ow>
15
29、ements IPersonService { 6 @Override 7 public String HelloWorld(string name){ 8 return "Hello "+name; 9 } 10 } 客户端的生成工具有多种,其中一种是使用 Axis 1.x 中的自带生成器 WSDL2Java ,此生成器可以根据 wsdl 文件生成客户端。 首先在环境变量中把 Axis_Home 绑定到 Axis 1.x 的根目录,在 path 加入设置 ".;%Axis_Home%\lib ", 然后输入
30、 Java org.apache.axis.wsdl.WSDL2Java http://localhost:8080/axis.server/services/PersonService?wsdl -p axis.client.person 此时系统将在 axis.client.person 包内生成客户端代码类 PersonServiceImpl、PersonServiceImplService、PersonServiceImplServiceLocator、PersonServiceSoapBindingStub。 PersonServiceImplServiceLocator 用于
31、实现 PersonServiceImplService 接口,它绑定了服务的名称,地址,端口等详细资料。 PersonServiceImpl 用于定义服务的接口,而 PersonServiceSoapBindingStub 则是此服务代理,它通过 SOAP 协议把操作请求发送到服务器,并把返回信息转化为 Java 对象。 1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, ServiceException { 3
32、 // TODO Auto-generated method stub 4 HelloWorld(); 5 } 6 7 private static void HelloWorld() throws 8 RemoteException, MalformedURLException{ 9 PersonServiceImpl personService=new PersonServiceSoapBindingStub( 10 new URL("http://loca
33、lhost:8080/axis.server/services/PersonService"), 11 new PersonServiceImplServiceLocator()); 12 System.out.println(personService.helloWorld("Leslie")); 13 } 通过系统自动生成的代理类就能简单地调用远程服务,这是因为在代理类中已经完成了大量关于PersonService 服务的配置,但本人觉得想要深入地了解 Axis 的开发,就应该了解其内部的结构。所以在下面例子当中将介绍如何使用Ax
34、is的内部机制直接调用Web服务。 在 org.apache.axis.client 中存在着服务的基础类 Service ,通过 Service 可用于管理服务的绑定地址,端点,获取服务的 WSDL 等详细信息。另外 Call 类用于管理每个服务请求动作,它可以设置每个请求的方法,最后通过call.invoke(Object[])调用服务,并获取完成后的返回值。 1 public static void main(String[] args) throws 2 RemoteException, MalformedURLException, Servic
35、eException { 3 HelloWorld(); 4 } 5 6 private static void HelloWorld() throws 7 ServiceException,RemoteException{ 8 //生成服务对象Service 9 Service service=new Service(); 10 Call call=(Call) service.createCall(); 11 //设置Endpoint地址 12
36、 call.setTargetEndpointAddress( 13 "http://localhost:8080/axis.server/services/PersonService"); 14 //绑定请求方法名称 15 call.setOperationName("HelloWorld"); 16 //通过call.invoke调用服务,获取返回值 17 String data=(String)call.invoke(new Object[]{"Leslie"}); 18
37、 System.out.println(data); 19 } 如果觉得使用 Call 实现请求较为麻烦,Service 中还提供一个 getPort 方法,通过此方法还可直接实现服务接口 PersonServiceImpl。另外,Axis 还为准备了一个 ServiceFactory 工厂,通过 ServiceFactory 可以直接获取 Service 对象。 1 public static void main(String[] args) throws 2 RemoteException, MalformedURLExc
38、eption, ServiceException { 3 // TODO Auto-generated method stub 4 HelloWorld(); 5 } 6 7 private static void HelloWorld() throws 8 ServiceException, RemoteException, MalformedURLException { 9 String wsdl="http://localhost:8080/axis.server/servi
39、ces/PersonService?wsdl"; 10 String uri="http://localhost:8080/axis.server/services/PersonService"; 11 String serviceName="PersonServiceImplService"; 12 13 //使用serviceFacotry直接生成服务 14 ServiceFactory factory=ServiceFactory.newInstance(); 15 Servic
40、e service=(Service) factory.createService( 16 new URL(wsdl),new QName(uri,serviceName)); 17 18 //使用service.getPort方法实现服务接口 19 PersonServiceImpl personService=(PersonServiceImpl)service 20 .getPort(PersonServiceImpl.class); 21 Str
41、ing data=personService.helloWorld("Leslie"); 22 System.out.println(data); 23 } 2.3 以自定义对象传输数据 若需要以自定义对象作为数据传输的载体,则需要为自定义对象继承 Serializable 接口。另外可以留意一下服务的 wsdl , 因为 Axis 并没有默认使用List , Map 等类型, 在 List,Map 等作为参数时,wsdl 都会把返回类型设置为 ArrayOf_xsd_anyType,所以建议使用简单数组作为返回值。 1 public clas
42、s PersonEntity implements Serializable { 2 private Integer id; 3 private String name; 4 private Integer age; 5 private String address; 6 7 public PersonEntity(Integer id,String name,Integer age,String address){ 8 this.id=id; 9 this.nam
43、e=name; 10 this.age=age; 11 this.address=address; 12 } 13 14 public Integer getId(){ 15 return id; 16 } 17 18 public void setId(Integer id){ 19 this.id=id; 20 } 21 .......... 22 } 23 24 public interface
44、IPersonService { 25 PersonEntity GetPerson(int id); 26 PersonEntity[] GetList(); 27 List GetList(String name); 28 } 29 30 public class PersonServiceImpl implements IPersonService { 31 @Override 32 public PersonEntity[] GetList(){ 33 PersonEntity[] list=
45、new PersonEntity[2]; 34 PersonEntity person1=new PersonEntity(1,"Leslie",32,"tianhe"); 35 PersonEntity person2=new PersonEntity(2,"Elva",31,"henan"); 36 list[0]=person1; 37 list[1]=person2; 38 return list; 39 } 40 41 @Override 42
46、 public List GetList(String name){ 43 List list=new ArrayList(); 44 PersonEntity person1=new PersonEntity(1,name+" Lee",32,"tianhe"); 45 PersonEntity person2=new PersonEntity(2,name+" Chen",31,"henan"); 46 list.add(person1); 47 list.add(person2);
47、 48 return list; 49 } 50 51 @Override 52 public PersonEntity GetPerson(int id){ 53 return new PersonEntity(id,"Leslie",32,"tianhe"); 54 } 55 } 在 server-config.wsdd 中使用 beanMapping 加入自定义对象绑定,以 languageSpecificType 绑定类名,qname 可由用户设置,但必须与 xmln 特性相
48、对应。
1 .........
2 49、 qname="myNS:PersonEntity" xmlns:myNS="urn:PersonEntity"
7 languageSpecificType="java:axis.entity.PersonEntity" />
8






