[web Server技术]axis序列化与反序列化 |
狂飙的蜗牛 发表于 2006/6/20 9:50:25 |
开发自己的序列化/反序列化器是一个激动人心的工作,但是却并不复杂,需要做的事情包括实现名成为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 classJDomElementDeserializerFactory 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 Sample1implements 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.0Content-Type: text/xml; charset=utf-8Accept: application/soap+xml, application/dime, multipart/related, text/*User-Agent: Axis/1.1Host: 127.0.0.1Cache-Control: no-cachePragma: no-cacheSOAPAction: ""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 OKContent-Type: text/xml; charset=utf-8Date: Wed, 31 Mar 2004 06:42:18 GMTServer: Apache Coyote/1.0Connection: 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服务的工作机理,让您真正驾驭Axis。
|
|
|

.: 公告
|
« | September 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | | | | | |
|
.: 我的分类(专题)
|

.: 最新日志
.: 最新回复
|

blog名称:狂飙的蜗牛 日志总数:31 评论数量:19 留言数量:0 访问次数:158450 建立时间:2006年6月13日 |
|

.: 留言板
|

.: 链接
|

|