分享
 
 
 

Web Service深度编程——Axis序列化/反序列化器开发指南

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Web Service深度编程——Axis序列化/反序列化器开发指南

作者:薛谷雨

作者简介

薛谷雨是NORDSAN(北京)信息科技开发有限公司高级JAVA研发工程师,正致力于企业级异构数据交换的服务器产品的研发,在J2EE和WEB SERVICE方面有较为丰富的开发经验,你可以通过 rainight@126.com 与他取得联系。

前言

Axis是Apache组织推出的SOAP引擎,Axis项目是Apache组织著名的SOAP项目的后继项目。Axis为开发者提供了大量的序列化/反序列化器,能够基本满足大部分应用。但在某些情况下,对特定的对象,现有的序列化/反序列化器不能胜任,于是只有开发人员自己实现专用于此对象的序列化/反序列化器插入到Axis中来完成序列化工作。考虑到Web Service是一门新兴技术,中文资料大多是泛泛的讲解,关于序列化/反序列化器的开发鲜有较为深入的介绍,本文提供一份较为完整的开发指南,并提供了一个十分有用的实现,即序列化JDOM模型的Element,使其可以通过Web 服务在网络上传输,我想这一扩展是许多采用JDOM作为XML解析工具的开发人员都梦寐以求的功能。通过本文的介绍和实例,希望能起到抛砖引玉的作用,读者在阅读完本文之后可以轻松的实现针对于任何非符合BEAN规范的对象的序列化/反序列化器。

本文所面对的读者需要有一定的使用Axis做Web服务开发的开发经验,因此关于如何Axis的基础知识并不在本文的介绍范围,如果读者对此感兴趣,可以参考本文最后的参考资料部分,去相应的网站进行学习。

序列化/反序列化器简介

序列化/反序列化器在英文中的对应翻译是Serializer/Deserializer,一个序列化器的功能是遵循一定的映射规则和编码风格,将一种类型的JAVA对象通过某种特定的机制,转换成为XML描述的形式;反序列化器的功能是序列化器所做工作的逆操作,两者相辅相成,成对出现。Axis中的序列化/反序列化器采用设计范式中的工厂模式,每一个Serializer唯一对应一个SerializerFactory;每一个Deserializer唯一对应一个DeserializerFactory。一种类型的JAVA对象具体要采用哪个序列化/反序列化器需要在提供Web服务的服务器和调用Web服务的客户端分别配置,关于这一部分如何配置,我将在本文后面的内容中进行详细介绍。Axis已经为开发者提供了丰富的序列化/反序列化器,对于java的基本数据类型,绝大部分常用的容器类(比如数组类型,Vector类型等)都提供了实现,特别是提供了对W3C的DOM对象(比如Document, Element等)和符合Bean规范的JAVA对象提供了功能完善的序列化/反序列化器,因此我们在需要的时候只要在配置文件中配置一下就可以直接使用。如果对象中包含其它类型的对象,比如Vector中包含一组Bean对象,Axis会自动叠代的调用序列化器,最终拼装成唯一的XML表述。在还原成JAVA对象时,也遵循这样的叠代操作逆向进行。关于Axis到底内置了哪些序列化/反序列化器,您可以参照Axis的API文档中包org.apache.axis.encoding.ser下的类的名称"望文生义"的了解一下,在以后的开发中做到心中有数。但对于一些特殊类型的对象(其实我们自己开发的很大一部分类都是这种特殊类型的对象,很少有绝对符合Bean规范的),需要通过Web服务进行传递,我们不得不开发自己的序列化/反序列化器。

开发篇

开发自己的序列化/反序列化器是一个激动人心的工作,但是却并不复杂,需要做的事情包括实现名成为org.apache.axis.encoding的包中的SerializerFactory,Serializer,DeserializerFactory和Deserializer这四个接口。下面我将结合一个实例来讲解序列化/反序列化器的开发方法,希望读者能够一边参看本文提供的源代码一边学习。

JDOM作为一款比较"另类"的XML解析工具(因为它不符合W3C的DOM模型,自己另立一套)默默地占领着java世界里的xml解析器的半壁江山,由于其简洁的设计和方便灵活的API调用,已经渐渐成为了许多开发人员在进行XML开发的首选。但是Axis是建立在W3C的DOM模型的基础之上,师出名们正派,自然不屑与JDOM为伍。因此当开发人员想将自己已经写好的基于JDOM的应用模块采用Web服务的方式发布的时候,不可避免的会遇到如何将JDOM模型下的对象如Document, Element等序列化的问题。在软件工程师不会自己扩展Axis的序列化/反序列化器的时候,我们只能有两个办法达到这个目的,第一个就是更改以前应用模块内的API设计,使暴露的入口参数和返回值参数都是W3C的对象类型,但这种做法并不现实,因为这一应用模块往往不是独立存在,牵一发将动全身,导致旧有系统架构的崩塌;另一种做法就是为这个模块做一个代理类,它做的工作就对外接收或返回DOM模型的对象,对内转换成JDOM模型的对象,然后转发给应用模块,繁琐且效率低下。当我们向Axis注入了针对于JDOM模型的序列化/反序列化器后,这一工作便可以由Axis代劳了。下面我们将逐个开发这四个类:

JDomElementSerializerFactory

JDomElementSerializerFactory是一个工厂类,需要通过某种机制注册到Axis引擎(具体方法见下面"服务器端应用篇");Axis通过调用它,来实例化JDomElementSerializer。Axis 提供了BaseSerializerFactory,这个类是一个抽象类,并实现其中包含了一些可重用的代码。我们自己开发的工厂类只需简单继承这个类就可以。构造函数中需要调用父类的构造函数将序列器类下面是它的源代码:

package org.apache.axis.encoding.ser;

public class JDomElementSerializerFactory

extends BaseSerializerFactory {

public JDomElementSerializerFactory() {

super(JDomElementSerializer.class);

}

}

JDomElementSerializer

JDomElementSerializer实现org.apache.axis.encoding.Serializer接口,其核心API是serialize(),我们需要在这个方法的内部完成对JDOM模型的Element的序列化工作,序列化的结果要保存在入口参数传入的序列化上下文对象(SerializationContext)中:

public void serialize(QName name, Attributes attributes, Object value,

SerializationContext context) throws java.io.IOException {

if (!(value instanceof Element))

throw new IOException(

Messages.getMessage("cant Serialize Object"));

//获取符合JDOM的Element对象

Element root=(Element)value;

//输出到StringWriter

XMLOutputter outputter=new XMLOutputter();//创建一个JDOM的XML输出器

StringWriter sw=new StringWriter();

outputter.output(root,sw);

//用支持W3C的DOM模型的Xerces解析器解析文本流

DOMParser parser=new DOMParser();//创建一个DOM的XML解析器

try {

parser.parse(new org.xml.sax.InputSource(

new java.io.StringReader(sw.toString())));

}catch (Exception ex) {

throw new java.io.IOException("序列化时产生错误");

}

//获取符合DOM模型的Element对象

org.w3c.dom.Element w3c_root =

parser.getDocument().getDocumentElement();

//放入序列化上下文对象中

context.startElement(name, attributes);

context.writeDOMElement(w3c_root);

context.endElement();

}

JDomElementDeserializerFactory

反序列化器的工厂类同序列化器的工厂类一样的设计,在此不在赘述。代码:

package org.apache.axis.encoding.ser;

public class JDomElementDeserializerFactory

extends BaseDeserializerFactory {

public JDomElementDeserializerFactory() {

super(JDomElementDeserializer.class);

}

}

JDomElementDeserializer

用过SAX解析XML的读者,对反序列化的实现比较容易理解,反序列化也采用了消息触发的机制,我们只需继承org.apache.axis.encoding.DeserializerImpl类,并覆盖其中的onEndElement方法:

/**

* 在元素结束触发反序列化的方法

* @param namespace String 命名空间

* @param localName String 本地名称

* @param context DeserializationContext 反序列化上下文

* @throws SAXException

*/

public void onEndElement(String namespace, String localName,

DeserializationContext context) throws SAXException {

try {

//从反序列化上下文对象中获取原始的消息元素

MessageElement msgElem = context.getCurElement();

if (msgElem != null) {

MessageContext messageContext = context.getMessageContext();

Boolean currentElement = (Boolean) messageContext.getProperty(

DESERIALIZE_CURRENT_ELEMENT);

//如果当前的消息元素本身需要反序列化

if (currentElement != null && currentElement.booleanValue()) {

org.w3c.dom.Element element = msgElem.getAsDOM();

org.jdom.input.DOMBuilder db=new org.jdom.input.DOMBuilder();

value=db.build(element);

messageContext.setProperty(DESERIALIZE_CURRENT_ELEMENT,

Boolean.FALSE);

return;

}

//反序列化消息元素中的消息体

java.util.ArrayList children = msgElem.getChildren();

if (children != null) {

//取得消息体

msgElem = (MessageElement) children.get(0);

if (msgElem != null) {

org.w3c.dom.Element ret = msgElem.getAsDOM();

org.jdom.input.DOMBuilder db=new org.jdom.input.DOMBuilder();

//用DOMBuilder将DOM模型的Element,转换成JDOM模型的Element

value=db.build(ret);

}

}

}

}

catch (Exception ex) {

//错误,则记日志,并抛SAXException

log.error(Messages.getMessage("exception00"), ex);

throw new SAXException(ex);

}

}

完成这四个类的编码,序列化/反序列化器的开发工作基本完成,下面将详细讲解使用及部署方法。

服务器端应用篇

为了简单起见,我们将一个很简单的类通过Web服务发布,类中只有一个名称为hello函数,函数的返回值为JDOM模型的Element。代码如下:

package test;

import org.jdom.*;

import java.rmi.RemoteException;

public class Sample1 implements java.rmi.Remote{

public Sample1() {

}

public Element hello(String name){

Element root=new Element("root");

Element hello=new Element("hello");

hello.setText("hello,"+name+"!");

root.addContent(hello);

return root;

}

}

关于如何将一个类发布成Web服务,在此并不进行介绍,相信读者可以自己完成,我们只关注如何将序列化/反序列化器加入到我们的Web服务中。打开web服务的配置文件server-config.xml,编辑关于Sample1的服务的配置部分:

<service name="Sample1" type="" regenerateElement="true"

provider="java:RPC" style="rpc" use="encoded">

<parameter name="scope" value="Request" regenerateElement="false"/>

<parameter name="className" value="test.Sample1" regenerateElement="false"/>

<parameter name="allowedMethods" value="*" regenerateElement="false"/>

<typeMapping

encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

qname="ns1:Element"

languageSpecificType="java:org.jdom.Element"

serializer=

"org.apache.axis.encoding.ser.JDomElementSerializerFactory"

deserializer=

"org.apache.axis.encoding.ser.JDomElementDeserializerFactory"

name="Element" regenerateElement="true"

xmlns:ns1="http://jdom.org"/>

</service>

注意上面代码中的粗体字部分,是我们现在要添加的,它表述了如何将序列化反序列化器部署到Web服务中。

部署到Web Server

解压缩本文后面附带的源代码,根目录下有build.xml文件,读者需要正确安装配置好Apache Ant,然后运行

Ant make

编译后可生成压缩文件sample.war。将生成的war包部署到Tomcat4.1下,启动Tomcat,本文默认的Tomcat监听的http端口为8080。后面的客户端测试程序也将通过连接这一端口访问此Web服务。如果读者的Tomcat不在8080端口上工作,那么客户端程序也要进行相应的修改。最后启动Tomcat,这部分操作完成。

客户端应用篇

下面我们将编写客户端程序访问刚才部署的Web服务,讲解如何把我们编写的序列化/反序列化器加载到客户端应用程序中,下面是客户端调用的代码,注意斜体字部分,是关于序列化/反序列化器的注册过程(如果你的Web服务器不是工作在8080端口,或采用了其他Web服务名,请自行更改下面程序中的url变量中的值),我们在test包下创建了一个名称为 Client的类,代码如下:

package test;

import org.apache.axis.client.Service;

import org.apache.axis.client.Call;

import org.apache.axis.utils.Options;

import javax.xml.namespace.QName;

public class Client {

public Client() {

}

public static void main(String[] args) throws Exception{

if(args.length<1){

System.out.println("错误:缺少参数");

System.exit(0);

}

//Web服务的URL

String url="http://localhost:8080/sample/services/Sample1";

Service service=new Service();

Call call = (Call)service.createCall();

call.setTargetEndpointAddress(url);

//注册序列化/反序列化器

call.registerTypeMapping(org.jdom.Element.class,

new QName("http://jdom.org","Element"),

new org.apache.axis.encoding.ser.JDomElementSerializerFactory(),

new org.apache.axis.encoding.ser.JDomElementDeserializerFactory());

//设置调用方法

call.setOperationName(

new javax.xml.namespace.QName("http://test", "hello"));

//Web服务调用

java.lang.Object _resp = call.invoke(new java.lang.Object[] {args[0]});

//输出到屏幕

org.jdom.output.XMLOutputter out=new org.jdom.output.XMLOutputter();

out.output( (org.jdom.Element) _resp, System.out);

}

}

编译后运行该程序,在控制台窗口工程的根目录下输入

run world ( 其中"world"为调用例程中API的入口参数)

经过一次web通讯,一两秒后屏幕将显示运行结果:

<root>

<hello>hello,world!</hello>

</root>

至此我们完成了一次Web服务的访问过程。如果在程序执行过程中,我们用TCP Moniter之类的工具监视这一次访问中的在网络中流入流出的数据,可以看到客户端发起调用的xml数据流如下:

POST /sample/services/Sample1 HTTP/1.0

Content-Type: text/xml; charset=utf-8

Accept: application/soap+xml,

application/dime, multipart/related, text/*

User-Agent: Axis/1.1

Host: 127.0.0.1

Cache-Control: no-cache

Pragma: no-cache

SOAPAction: ""

Content-Length: 430

<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv=

"http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<soapenv:Body>

<ns1:hello soapenv:encodingStyle=

"http://schemas.xmlsoap.org/soap/encoding/"

xmlns:ns1="http://test">

<name xsi:type="xsd:string">world</name>

</ns1:hello>

</soapenv:Body>

</soapenv:Envelope>

服务器端返回的结果的XML输出流如下:

HTTP/1.1 200 OK

Content-Type: text/xml; charset=utf-8

Date: Wed, 31 Mar 2004 06:42:18 GMT

Server: Apache Coyote/1.0

Connection: close

<?xml version="1.0" encoding="UTF-8"?>

<soapenv:Envelope xmlns:soapenv

"http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<soapenv:Body>

<ns1:helloResponse soapenv:encodingStyle=

"http://schemas.xmlsoap.org/soap/encoding/"

xmlns:ns1="http://test">

<ns1:helloReturn href="#id0"/>

</ns1:helloResponse>

<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle=

"http://schemas.xmlsoap.org/soap/encoding/"

xsi:type="ns2:Element"

xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"

xmlns:ns2="http://jdom.org">

<root>

<hello>hello,world!</hello>

</root>

</multiRef>

</soapenv:Body>

</soapenv:Envelope>

结语

以上详细讲解了Axis的序列化/反序列化器的开发过程,相信读者已经从中学到了不少知识,并能够应用于自己的项目开发中去。通过掌握这一技术,我们将更为深刻的理解Axis的内部结构和Web服务的工作机理,这些经验是市面上那些泛泛的讲解JAVA Web服务的参考书上所学不到的。后续的文章还将向您展示一些在Java Web服务深度开发中的高级技术,让您真正驾驭Axis。

参考资料

附件中提供了本文中的全部源码,它是一个完整的工程,可以参考学习。

在apache的网站您可以获得最新的Axis库及其开发文档:http://ws.apache.org/axis/ 本文中提到的Tomcat Web服务器可以在这里免费获得:http://jakarta.apache.org/tomcat/index.html

《IBM DEVELOPWORK》中一个关于web服务的教程《从JAVA类创建web服务》,是您快速掌握Web服务开发的很好教材,初学者可以进行学习。

JDOM类库可以去www.jdom.org下载,CSDN上有一篇不错的文章可以让您快速掌握JDOM的使用方法。http://www.csdn.net/develop/Read_Article.asp?Id=13806

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有