引言
Java对象的序列化(serialization)是对象的持久存储和对象状态的网络传输的 关键环节,在RMI(Remote Method Invocation)、JMS(Java Message Service)和EJB(EnterpriseJavaBeans)中都有应用。对象序列化可以实现分布式对象,例如RMI要利用对象序列化运行远程主机上的服务,就像在本地机器上调用对象一样。对象序列化还可以实现对象状态和数据的持久存储,在对象序列化的过程中,不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据,从而记录整个对象层次结构和 数据,以便保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。JDK(JavaTM 2 SDK)提供了实现对象序列化功能的机制,开发人员可以利用ObjectOutputStream类的writeObject()方法把对象写入字节流来实现对象的序列化(serialization),利用ObjectInputStream类的readObject()方法可以从字节流中读取对象实现 对象的反序列化(deserialization)。
但是,在JDK所提供的序列化机制中,类必须实现java.io.Serializable接口或者java.io.Externalizable 接口,其对象才可以被序列化,对于已有的系统,如果要实现系统中对象的网络传输,就会受到类型的限制从而导致直接序列化传输无法实现。此外,JDK的对象 序列化机制是以二进制格式将对象写入字节流的,数据格式依赖于JVM(JavaVirtual Machine)的实现,缺乏可读性和通用性,限制了对象数据的存储和管理。
随着Internet的发展,XML(Extensible Markup Language)已成为进行对象形式化描述和分布式数据交换的标准。XML具有层次结构,很适合存储对象的状态数据,而且XML格式的数据具有很强的可读性和通用型,所以XML很适合用来实现对象的序列化,可以弥补JDK所提供的序列化机制的不足。越来越多的应用场合需要直接从XML文档中创建对象或者 把对象状态保存成XML文档。
本文介绍了应用XML实现Java对象序列化的研究,分析了用XML建模实现对象序列化和用XML数据绑定技术实现对象序列化的方法以及用XML实现Java对象序列化的优势和局限。本文的后续篇幅共分为三部分,第一部分介绍构建XML模型实现Java对象序列化的方法,第二部分介绍用XML数据绑定实现对象序列化的方法,最后一部分是总结。
1.构建XML模型实现Java对象序列化
1.1 XML模型
随着Internet的发展,XML已成为进行对象形式化描述和分布式数据交换的标准。XML具有层次结构,很适合存储对象的状态数据和实现对象的 序列化。在对象序列化的过程中,不仅需要保留一个对象的数据,而且需要递归保存对象引用的每个对象的数据,记录整个对象层次结构和数据。一般来说,一个对象会对应一棵层次结构的对象树,对象的序列化需要完整地保留对象树的结构和数据信息。用XML实现对象的序列化,关键在于构建合适的XML数据模型,完备地描述对象树的信息,同时还要便于对象的反序列化。
XML的层次结构很适合构建描述Java对象的数据模型,但是,简单的利用XML的层次结构还不足以完整地描述Java对象的结构层次和数据信息,还需要针对Java对象的层次结构作详细的定义。在用XML构建数据模型时,需要考虑以下几个方面的问题:
(1) 继承关系
类的继承关系构成了面向对象的基本层次结构。支持Java对象序列化的XML模型需要记录类的完整的继承关系,记录和类结构相关的数据信息,在反序列化时要能够重新构建对象的类的继承关系,恢复数据。
(2) 嵌套定义
Java对象的数据成员可能是基本数据类型、Java对象和数组类型,所以Java对象一般都会引用到其他Java对象,在对象序列化时需要递归保存每一个所引用到的对象的数据,这就要求XML数据模型能够支持嵌套定义,记录对象引用的层次结构和对象数据。
(3) 数组的描述
Java对象的数据成员可能是数组类型。数组在Java中也是一个对象,但是数组对象没有显式的构造函数和类定义,所以不能像一般对象一样定义和处理。在用XML建模时需要对数组类型独立定义,描述数组的整体信息和数组成员的对象数据。
(4) 特殊元素的定义
在用XML建模时需要定义基本数据类型和空对象(即null)等元素。
1.2 已有的实现
(1) JosML (Java Object Serialization Markup Language)[2]
文献[2]中提出了用于Java对象序列化的XML模型JosML,该模型定义了基本数据类型,对象的嵌套关系和描述数组元素的模型,可以基本上实现Java对象的序列化。但是,这个模型没有定义对象的类继承关系,无法记录和类型相关的数据信息。
(2) JSX (Java Serialization to XML)[3]
JSX是一个用XML序列化Java对象的工具。JSX的XML模型定义了类的继承关系和对象引用的嵌套关系,包含了数组类型的定义模型和基本数据类型、空对象、字符串类型的对象等元素的定义,是一个比较完备和成熟的Java对象序列化的XML模型。JSX可以完整地记录Java对象的层次结构和对象数据以及继承关系,从而能够用XML格式成功地实现所有Java对象的序列化和反序列化。
1.3 存在的问题
对象的序列化过程中需要动态获得对象的状态,Java平台提供的反射机制可以实现这个要求。Java平台内建的序列化机制虽然也可以得到对象的运行时状态,但是序列化结果是二进制格式,而且只限于Serializable对象,所以基于XML模型的Java对象序列化的实现一般都通过反射 (reflection)机制完成。JosML和JSX在实现时都用到了反射机制。但是,用反射机制访问类的非公有成员以及调用非公有的构造方法时,如果 当前运行环境有安全策略限制,运行时会抛出相应的SecurityException,导致序列化无法正常进行。由于Java安全模型的限制,在安全要求比较高的运行环境中,利用反射机制实现的XML格式的Java对象序列化工具的应用将会受到限制。
2.应用XML数据绑定实现Java对象序列化
2.1 XML数据绑定
XML数据绑定是指将XML文件的描述文档(DTD或者XML Schema)映射为一系列的应用类。对Java的XML数据绑定而言,就是将XML文件的描述文档映射为一系列的Java类。通过读取文档把应用类实例 化为一棵对象树,便于运行时访问。相比较XML文档模型而言,XML数据绑定简单直观,适合于文档数据的应用。XML数据绑定包括三个方面的内容:一是编组(Marshalling),在内存中为对象生成 XML 表示,即用XML序列化Java 对象的过程;二是解组(Unmarshalling),是与编组相反的过程,在内存中根据 XML 表示构建一个对象;三是验证(validate),包括两方面,解组时要求XML数据文件是良定义的,符合生成java源文件的Schema约束,编组时 要求内存中的java类也是可编组的。所以,XML数据绑定技术和工具也可以用来实现XML格式的对象序列化。
2.2 实现框架
XML数据绑定的研究已经有了很多成果,许多组织都推出了实现XML数据绑定的框架,如JAXB、Castor等,JavaCommunity Process(JCP)中的JSR-031 也正致力于定义一个标准,确定标准的接口。下面是目前Java平台上主要应用的一些XML数据绑定的实现框架:
(1) JAXB(Java Architecture for XML Binding)[4]
JAXB是SUN、IBM和BEA等多家联合发布的Java平台的数据绑定标准,并提供了参考实现。JAXB支持从XML Schema生成Java类,将 Java 语言代码绑定到 W3C XML Schema 文法所定义的文档,在此基础上,JAXB可以利用XML实例文档生成Java对象树,也可以将Java对象树的内容重新写到XML实例文档中,从而可以实 现基于XML的Java对象的序列化和反序列化。
(2) Castor[5]
Castor是一个开放源码的XML数据绑定的工具,支持根据XML Schema的绑定,同时也支持缺省绑定,可以完成从XML文档到已有JavaBean之间的直接序列化绑定,允许没有XML Schema参与工作。Castor在编组时只支持XML Schema所定义的数据类型,如果自定义的类作为数据成员,则必须定义映射文件。Castor要求绑定的Java类具有JavaBean的格式,即需要定义公共的缺省构造方法,定义公共的get和set方法用来访问属性的值。在解组时,如果数据成员没有写入访问接口(set方法),结果对象就不会有该数 据成员的数据。Castor在用于Java对象的序列化时也有其局限性。
(3) JiBX[6]
JiBX 数据绑定框架在结构和实现技术上和其他数据绑定框架有所不同。首先,JiBX没有使用SAX2解析器的API,而是采用了拉(pull)解析器的API。 其次,JiBX针对反射机制的性能劣势和功能局限,采用了字节码增强(Byte code enhancement)技术。此外,JiBX没有采用以XML为中心的方法根据XML Schema或者DTD的定义生成代码,而是采用了以Java为中心的方法,定义映射规则,实现映射绑定。[9]由于这个原因,JiBX无法直接应用到Java对象的序列化中。但是,采用字节码增强技术是用XML实现Java对象序列化的一个比较好的解决方案。
(4) Zeus[7]
Zeus是根据XML文档的DTD定义生成代码的一种XML数据绑定工具,和其他数据绑定工具相比,Zeus易于使用但功能有限。目前Zeus的版 本只支持DTD,在数据类型方面,Zeus 只支持 String,而不支持其它类型的值,例如int或Date,还缺乏对引用的支持,不能直接进行数据分解或编组图结构,只能对根据DTD生成的Java类 的对象作编组和解组操作。这也限制了Zeus在Java对象序列化上的应用。
(5) Quick[8]
Quick是一中比较灵活的XML数据绑定工具。和Zeus一样,Quick根据XML文档的DTD定义生成绑定的代码,但Quick比Zeus复杂,功能也比Zeus强大。Quick使用一系列相当复杂的步骤来根据 DTD 文档描述生成代码,在此过程中使用了作为中间步骤的三个独立的绑定模式文档:QDML文档提供文档描述,大致相当于 DTD,不过添加了一些类型和继承;QJML 文档定义了 XML 到Java 语言对象的绑定;QIML 文件基本上是 QJML 的编译形式,可以用它来生成实际的绑定代码。所以,应用Quick实现Java对象的序列化也要受到描述文档的限制。[9]
2.3 存在的问题
Java平台上的XML数据绑定框架致力于将XML数据和Java对象绑定,实现将XML实例文档转换成Java对象树。数据绑定框架在实现时,要 么根据Schema、DTD或其他映射文件生成自己的数据类,然后要求开发者在应用程序中使用,要么使用反射机制,通过使用 Java 虚拟机所知的类信息提供对数据的间接运行时访问。第一种实现方法由于受到Schema、DTD或映射文件的限制,不能应用于任意Java对象的序列化;第 二种实现方法则受到反射机制的限制。JiBX中所用到的字节码增强(Byte code enhancement)技术是解决这一问题的一个比较有效的方法。字节码增强技术也同样可以应用到构建XML数据模型序列化Java对象的实现中,解决 第二部分提到的应用反射机制的限制。
3.结论
应用XML序列化Java对象可以解决对象持久存储和网络传输的实际应用中所遇到的问题。直接构建XML数据模型和利用XML数据绑定技术都可以在 一定程度上实现基于XML的Java对象序列化,但在实现中都会受到反射机制的限制。虽然用字节码增强技术可以解决一定的问题,但从根本上来说,这一问题的出现是由于对象序列化的要求和Java平台安全模型之间的矛盾造成的。运行时强制访问对象的内部信息有悖于Java的安全模型。在解决对象序列化的问题 的同时,还需要在两者之间作取舍或折中。