JAXB技术的魔力 —— 二维CAD图象数据的存储

JAXB技术的魔力
                                                                              —— 二维CAD 图象数据的存储

SUN中国软件技术中心      王昱     wang.yu@sun.com
    摘要 :本文介绍了怎样使用JAXB 技术来快速开发基于XML 的应用程序,通过一个简单的二维图像数据存储的例子,展现了使用XML 数据表示给应用程序带来的灵活性,以及怎样使用JAXB 技术来简化XML 操作的复杂程度。
序言
    我们的一个合作伙伴,正在使用Java 技术开发专业领域内的CAD 二维图像应用软件。随着Java 技术的不断发展,其虚拟机的速度不断的提高,越来越多的厂商使用Java 语言作为他们图形软件跨平台的解决方案。特别是JDK1.4 推出以后,对图形图像的操作,性能有了显著的提高。在某些二维图形图像的类(如VolatileImage )中,能够直接操纵硬件加速的图像缓存,甚至使得Java 成为图形游戏的开发平台成为可能。
    为了加快开发的速度,我们的合作伙伴选择了Java 本身的对象序列化(serialization )技术来作为应用数据的存储。换句话说,应用程序中的所有图形图像,不管是 按钮 还是 线条 ,都以对象序列化方式存储到文件系统中;在使用的时候,从文件中读出数据,恢复成内存中的Java 对象。这样做的好处是简单,所有对象数据的存储和恢复都由Java 平台内部的序列化机制来完成,数据保存的格式(二进制)也由Java 来制定;并且在性能上,这种方法也是不错的,特别是在JDK1.4 上,对象序列化的速度有了很大的提高,使得序列化技术应用得越来越广泛,不仅仅用于对象数据的存储,还大量的应用于各种分布式网络计算模型中。
    但是,我们的合作伙伴在产品开发和版本升级过程中遇到了一些问题。
l          数据的兼容性差
在升级的版本中,如果对Java 类的改动超出了对象序列化兼容性的范围,如:改变了类图结构中的继承关系和层次结构,改变或增减了类成员变量数量和类型等等,都会造成新老版本的数据不一致性。很有可能新版本的系统不能兼容和使用老版本系统产生的数据。
l          对数据的操作性差。
由于序列化所产生的数据是由JVM 内部机制生成的二进制数据,对其进行修改和转换有一定的困难和风险。而且二进制数据的可读性比较差。而对这些图形数据的操作是此系统不可缺少的一部分。例如,不同版本之间的数据转换,不同格式之间的转换以及系统数据的导入和导出,都需要对系统数据进行不同程度的操作。
l          数据的保存量大。
用序列化保存的数据通常包含了大量无用的信息。例如,保存一个简单的"Button" ,序列化会保存它所有父类对象的所有实例的成员变量,还会保存这个对象所有缺省的其他属性。而实际上,我们只要关心这个"Button" 上面的文本,加上它在图面中的坐标就行了。要控制序列化保存数据量的大小需要较复杂和繁琐的设置,例如使用 transit 修饰符等等(详见 参考资料)。
l          JDK 版本的影响。
不同的JDK 的序列化实现有可能会有差异,保存数据的格式也有所不同,这就使得系统有可能被绑定在某个版本的JDK ,而不能使用高版本的JDK 所带来的性能上和功能上的好处!       
一. XML数据表示和JAXB的解决方案
    由于存在以上的问题,需要考虑采用其他的解决方案,以保证即能够保证数据操作的灵活性,又能够使用现有的数据格式和成熟的开发工具包,减轻系统开发和维护的负担。
    XML 成为被考虑的目标之一。显然,具有 通用数据表示 之称的XML 能够带来很大的灵活性,使得数据在各个平台之间的交换,各个版本之间的升级变得非常方便;而且,在XML 的开发中,Java 平台已经拥有很多成熟的软件开发工具和解决方案。一般来说,使用XML 作为系统数据存储的格式有以下两种解决方案:
l          直接使用Java 对象序列化的内部对XML 的支持功能:XmlEncoder XMLDecoder 两个类。这个功能其实是对象序列化功能本身的一部分,通过使用XmlEncoder XMLDecoder 这两个类,可以使序列化的数据以XML 的格式保存和恢复,而不是二进制格式。这种方式增加了对数据的可读性和可操作性。但是仍然不能灵活地按照自己的意愿和需求对数据进行保存和恢复。
l          自己定义XML 的数据格式。这个方案使系统拥有了最大的灵活程度。一旦使用自己定义的XML 格式,那么对此XML 格式的文件进行解析、效验以及反向解析(XML 文件的生成)都需要自己去开发了!一旦版本发生更新,XML 的数据格式也有可能随着改变,这些解析和效验程序都有可能需要做出相应的改动。这样会大大增加了系统开发的负担和周期!
    JAXB 正是为解决这些问题而提出来的。在允许你自己灵活定义自己的 XML 文件格式的基础上,由JAXB 替你生成操作XML 文件的源代码,使你的应用程序将重点放到Java 对象上,而不用直接面对XML 操作。这正是JAXB 的目的所在。
二. JAXB技术介绍
2.1 .什么是JAXB?
    Java  Architecture for XML Binding (JAXB) 是一个业界的标准,是一项可以根据XML Schema 产生Java 类的技术。该过程中,JAXB 也提供了将XML 实例文档反向生成Java 对象树的方法,并能将Java 对象树的内容重新写到XML 实例文档。从另一方面来讲,JAXB 提供了快速而简便的方法将XML 模式绑定到Java 表示,从而使得Java 开发者在Java 应用程序中能方便地结合XML 数据和处理函数。
    这意味着你不需要处理甚至不需要知道XML 编程技巧就能在Java 应用程序中利用平台核心XML 数据的灵活性。而且,可以充分利用XML 的优势而不用依赖于复杂的XML 处理模型如SAX DOM JAXB 隐藏了细节并且取消了SAX DOM 中没用的关系 —— 生成的JAXB 类仅描述原始模型中定义的关系。其结果是结合了高度可移植Java 代码和高度可移植的XML 数据。其中这些代码可用来创建灵活、轻便的应用程序和Web 服务。
2.2.  JAXB的体系结构
JAXB体系结构
图 1  JAXB体系结构
      JAXB 的体系结构和应用过程如图1 所示,一般来说包含以下几个步骤:
l          根据你的应用程序所要操作的XML 数据格式,撰写相应的XML Schema ,有关XML Schema 的知识,请参阅 参考资料
l          使用JAXB 所带的编译工具(Binding Compiler ),将这个XML Schema 文件作为输入,产生一系列相关的Java Class Interface
l          在使用JAXB 编译工具的时候,可以有选择性的提供一个配置文件(图1 的虚线部分),来控制JAXB 编译工具的一些高级属性。
l          这些Java Class Interface 是你的应用程序操纵XML 数据的主要接口和方法。
l          通过JAXB XML 文档进行的操作主要包括:将符合XML Schema 规定的XML 文档解析生成一组相应的Java 对象;对这些对象进行操作(修改、增加和删除对象的属性等等);然后将这些对象的内容保存到这个XML 文档中。
       下面我们结合本文的示例来进一步说明使用JAXB 的过程。
 
三. 实例分析
3.1. 示例运行的环境和步骤
本示例运行的Java 环境是JDK1.3.1 以上。本示例在Jdk1.4.1 运行测试通过。
l          展开此压缩文件,在bin 目录下找到可执行文件rundemo.bat for windows )或rundemo.sh for unix
l          确保环境变量JAVA_HOME 已经正确设置,指向你系统所安装的JDK 的目录。
l          运行rundemo.bat rundemo.sh ,就会出现如下图所示的运行画面。
3.2. 示例运行的场景
运行
图2  demo运行场景
               
如图2 所示,本示例演示了一个非常简单的场景,它允许你在画布上使用鼠标绘制大小一定的正方形或圆形。
l          你的鼠标点到什么位置,就会在相应的位置上绘制图形。
l          你可以通过(Color )菜单改变当前画笔的颜色:绿色或红色。
l          你可以通过(Graph )菜单改变当前图形的形状:正方形或圆形。
l          你还可以通过(File )菜单中的 save save as 子菜单将当前已经绘制的图形以XML 格式保存起来。
l          你还可以通过(File )菜单中的 open 子菜单,选择以前保存过的图形文件,将它显示在画布上。
3.3. JAXB使用过程分析
l          下载JAXB 开发工具包
      JAXB1.0 的正式版本的一个实现(大家一定要记住,JAXB 只是一个标准,Sun 公司提供的此工具包只能说是这个标准的一个实现)已经发布了。由于XML Web Services 中的大量应用,所以,JAXB1.0 作为Web Services 开发包的一部分,可以从 WSDP1.1 下载。其中jaxb 包含在jaxb-1.0 子目录下。
l          设置环境变量 
    要使用JAXB ,在下载JAXB 开发包以后,还要设置一些环境变量,主要是设置classpath 的路径,以提供JAXB 包所带的库文件的位置。一般来说,可以写一个专门用来设置环境变量的执行文件。例如,在Windows 上可以写这样一个 setenv.bat 的文件:
 
set JAVA_HOME=c:/application/java/jdk1.4.1_01
set JWSDP_HOME=c:/application/wsdp1.1
set JAXB_HOME=%JWSDP_HOME%/jaxb-1.0
set JAXB_LIBS=%JAXB_HOME%/lib
set JAXP_LIBS=%JWSDP_HOME%/jaxp-1.2.2/lib
set JWSDP_LIBS=%JWSDP_HOME%/jwsdp-shared/lib

set PATH=%JAXB_HOME%/bin;%JWSDP_HOME%/jwsdpshared/bin;%PATH%

set CLASSPATH=%JAXB_LIBS%/jaxb-api.jar;%JAXB_LIBS%/jaxb-ri.jar;%JAXB_LIBS%/jaxb-xjc.jar;%JAXB_LIBS%/jaxb-libs.jar;%JAXP_LIBS%/jaxb-api.jar;%JAXP_LIBS%/endorsed/xercesImpl.jar;%JAXP_LIBS%/endorsed/xalan.jar;%JAXP_LIBS%/endorsed/sax.jar;%JAXP_LIBS%/endorsed/dom.jar;%JWSDP_LIBS%/jax-qname.jar;%JWSDP_LIBS%/namespace.jar;.
l          确定XML Schema
    要确定XML Schema 意味着你要确定一个规则,来约束你的XML 文档,使所有符合这个规则的XML 文档看上去都很类似。例如,在这个例子中,我们希望XML 文档的格式如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<shapeContainer>
        <shape>
              <xposition>185</xposition>
              <yposition>83</yposition>
              <shapename>circle</shapename>
              <shapecolor>red</shapecolor>
        </shape>
        <shape>
              <xposition>169</xposition>
              <yposition>177</yposition>
              <shapename>circle</shapename>
              <shapecolor>green</shapecolor>
        </shape>
        <shape>
              <xposition>358</xposition>
              <yposition>155</yposition>
              <shapename>rect</shapename>
              <shapecolor>green</shapecolor>
        </shape>
</shapeContainer>
    文如其意,用不着太多的解释,大家就能明白上面的XML 文档所包含的意思。<shapeContainer> 表示了画布,在画布中有各种各样的形状,每个形状都包含了一些显示信息,例如x,y 坐标,形状的类型和颜色等。在应用程序中想要操纵此XML 文档,还要根据这个XML 文档,产生一个Schema 文档。例如:(此 schema下载
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:element name="appversion" type="xsd:string"/>
        <xsd:element name="shapeContainer" type="ShapeContainerType"/>
        <xsd:complexType name="ShapeContainerType">
              <xsd:sequence>
                      <xsd:element name="shape" type="ShapeType" minOccurs="1" maxOccurs="unbounded"/>
              </xsd:sequence>
        </xsd:complexType>
        <xsd:complexType name="ShapeType">
              <xsd:sequence>
                    <xsd:element name="xposition" type="xsd:int"/>
                    <xsd:element name="yposition" type="xsd:int"/>
                    <xsd:element name="shapename" type="ShapeNameType"/>
                    <xsd:element name="shapecolor" type="ShapeColorType"/>
              </xsd:sequence>
        </xsd:complexType>
        <xsd:simpleType name="ShapeColorType">
              <xsd:restriction base="xsd:string">
                      <xsd:enumeration value="green"/>
                      <xsd:enumeration value="red"/>
              </xsd:restriction>
        </xsd:simpleType>
        <xsd:simpleType name="ShapeNameType">
              <xsd:restriction base="xsd:string">
                    <xsd:enumeration value="circle"/>
                    <xsd:enumeration value="rect"/>
              </xsd:restriction>
        </xsd:simpleType>
</xsd:schema>
     这个Schema 描述了对xml 文档的约束。例如,
    <xsd:complexType name="ShapeContainerType">
              <xsd:sequence>
                      <xsd:element name="shape" type="ShapeType" minOccurs="1" maxOccurs="unbounded"/>
              </xsd:sequence>
        </xsd:complexType>
    它规定了<shapeContainer> 这个节点内可以包含一个或多个<shape> 的节点。而  
<xsd:complexType name="ShapeType">
       <xsd:sequence>
                <xsd:element name="xposition" type="xsd:int"/>
                <xsd:element name="yposition" type="xsd:int"/>
          <xsd:element name="shapename" type="ShapeNameType"/>
                <xsd:element name="shapecolor" type="ShapeColorType"/>
          </xsd:sequence>
   </xsd:complexType>
   则规定了每个 <shape> 节点必须包含 x,y 坐标、形状类型和颜色等属性。
   schema 的其他部分还规定了 颜色 属性由 红色 绿色 组成,形状 类型属性 圆形 方形 组成。另外,在 Schema 中还使用了一些专用的描述符,例如 “complexType” “simpleType” “element” “sequence” 以及大量的 "NameSpace" 的知识,我就不一一介绍了,想要详细了解 XML Schema ,请访问 参考资料
   Schema XML 文档之间的关系,就好象 Java 中类与实例的关系。每个符合 schema XML 文档,都是这个 Schema 的一个实例;而 Schema 本身是一个模板,它规定了 XML 文档应该是什么样的。
l          使用编译工具生成相应的Java
    有了Schema 文件以后,我们就可以利用JAXB 工具包,让它来替我们生成操纵符合这个Schema 规定的所有XML 实例文档的所有Java 源代码。
%JAXB_HOME%/bin/xjc.bat demo.xsd -d src -p epri.jaxb
      其中 
u        %JAXB_HOME% 是你安装JAXB 工具包的位置,通常在jwsdp 工具包的子目录下。
u        demo.xsd Schema 的文件名,一般以xsd 作为文件名的后缀。
u        -d   的选项,是指定系统生成的Java 源代码所放置的目录
u        -p   的选项,是指定系统生成的Java 源代码所在的Java Package 的名称。
u        还有更多的选项,请参考JAXB 的相关文档。
    如果运行成功的话,就会在你"-d" 选项指定的目录下产生一些java 代码。如果感兴趣的话,大家可以查看这些代码进行详细的研究。如果你就想知道怎样使用它们的话,那么只需要简单的了解就行了。
l          在应用程序中使用这些代码
    下面我们分析一下如何在我们的应用程序中使用JAXB 工具包替我们生成的代码。在我们的实例中,主要有两个Java 源文件: JaxbDemoFrame.java MyCanvas.java 。当然,大家也可以从 此处下载所有的源代码。

    JaxbDemoFrame.java 中大部分都是处理Frame 中组件的代码。包括画布、菜单的初始化,以及各种事件的处理代码。其中和JAXB 有关的有JAXB 初始化代码:
 126      //init jaxb
 127       try {
 128           JAXBContext jc = JAXBContext.newInstance( "epri.jaxb" );
 129          
 130           ObjectFactory objFactory = new ObjectFactory();
 131           myContainer = objFactory.createShapeContainer();
                       ......
 134           m = jc.createMarshaller();
 135           u = jc.createUnmarshaller();
 136           m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
 137         
 138          
 139       } catch( JAXBException je ) {
 140           je.printStackTrace();
 141       }
    JAXBContext 提供了一个入口,通过这个入口可以管理必要的XML/Java 绑定信息。客户端应用程序通过newInstance(contextPath) 方法得到该类的新实例。contextPath 参数包含一个或多个Java 包名 ,这些Java 包就是了JAXB 编译器所产生的接口代码所在的Java 包。该参数值初始化JAXBContext 对象,使得它能够管理JAXB 编译器产生的接口;而通过ObjectFactory ,可以直接生成根节点java 对象ShapeContainer ;对于Marshaller Unmarshaller ,这两个对象主要是用于Java 对象和XML 文档互相转换的主要接口(Marshaller 负责从Java 对象到XML 文档的转换,Unmarshaller 负责从XML 文档到Java 对象的转换)。
    在打开文件的操作代码中,也有对JAXB 的操作:
150    private void openFile()  {
151        FileDialog open = new FileDialog(this,"open file..",FileDialog.LOAD);
152        open.setVisible(true);
153        filename = open.getDirectory()+open.getFile();
154      try {
155          myContainer  =(ShapeContainer)u.unmarshal( new FileInputStream(filename));
156          this.canvas.setShapeContainer(myContainer);
157          this.canvas.repaint();
158      } catch( JAXBException je ) {
159          je.printStackTrace();
160      } catch (FileNotFoundException fe) {
161          System.out.println("file not found!");
162      }
163    }

Unmarshaller.unmarshal() 的方法可以直接将一个 XML 文件里的数据转换成 Java 对象。此对象应该是整个 java 对象树中最靠近根的对象(在本例中为 ShapeContainer )。其他的对象都可以通过这个根对象的其他方法进一步获得,下面的章节会介绍到。
在文件保存的操作代码中,也有对JAXB 的操作:
182   private void saveAsFile() {
            ......
187         m.marshal(myContainer, new FileOutputStream(filename));
            ......
193    }
Marshaller.marshal() 的方法是将一个根节点的Java 对象,连同这个对象内部所包含的其他所有对象,都按照Schema 的要求将对象中的内容输出到XML 文档中去。
MyCanvas.java 主要是画布的绘图操作,包含了鼠标事件的响应和绘制屏幕的方法中。在MyCanvas.java 也有一些代码使用了JAXB 生成的class
53  public void mousePressed(MouseEvent e) {
        .......

65        java.util.List shapeList = myContainer.getShape();
66        try {
67             ShapeType newType = this.objectFactory.createShapeType();
68            newType.setShapecolor(this.currentColor);
69            newType.setShapename(this.currentgraph);
70            newType.setXposition(this.currentX);
71            newType.setYposition(this.currentY);
72            shapeList.add(newType);

73        } catch( JAXBException je ) {
74            je.printStackTrace();
75        }
76        repaint();
77    }

90  public void paint(Graphics g) {
91        if (myContainer !=null){
92            java.util.List shapeList = myContainer.getShape();
            ......  
           
95            for( Iterator iter = shapeList.iterator(); iter.hasNext(); ) {
96                ShapeType  myshape =  (ShapeType)iter.next();
97                String shapename = myshape.getShapename();
98                String shapeColor = myshape.getShapecolor();
99                int xposition = myshape.getXposition();
100               int yposition = myshape.getYposition();
101              
 if (shapeColor.equals("red"))  g.setColor(Color.red);
102               if (shapeColor.equals("green"))  g.setColor(Color.green);
103               if(shapename.equals("circle")) g.drawOval(xposition-25,yposition-25,50,50);
104                if(shapename.equals("rect")) g.drawRect(xposition-25,yposition-25,50,50);
105                this.setForeground(currentcolor);
106            }
107        }
108    }

在鼠标点击事件的处理中和屏幕绘制的方法中有大量使用 JAXB 所生成的类的代码(黑体字所表示的)。这些代码比较清楚的表现了怎样使用 JAXB 所生成的类。鼠标点击事件的处理中,根据程序的逻辑,每次鼠标的点击都应该根据鼠标当前的位置,画笔的当前颜色和当前的形状,来创建一个图形 , 并将此图形添加到整个对象树中去;而在屏幕绘制的 paint() 方法中,应用程序遍历整个对象树,找到每个图形的属性,并将它们绘制在屏幕上。
 
JAXB 所生成的代码都有一定的规律(遵循 JAXB 标准),这些规律非常简单易用。例如,在 Schema 中我们规定了,在 ShapeContainer 中允许有一个或多个 shap 节点,那么在生成的 ShapeContainer 这个类中就 getShape() 方法来返回一个 shape 的集合。又比如,在 Schema 中我们规定了每个 shape 都有一些属性( shapename, shapecolor, xposition, yposition )。那么在相应的 ShapeType 的类中,我们就会看到 get set 的一些方法去设置和获得这些属性的值。
四. 使用XML和JAXB的优势
l          简单易用。
通过上面的例子,大家可以发现所有对XML 文档的操作都隐藏起来了,你的应用程序不用使用SAX DOM 接口编程去操纵XML 文档,使程序量降低,错误率减少,有助于程序质量的提高。
l          维护性好。
当你的产品需要更新,或是数据模型需要改变时,只需要重新定义Schema ,然后让JAXB 重新生成对XML 文档进行操纵的类,使得你的应用程序具有很好的维护性。
l          数据兼容性好。
当你进行版本更新的时候,往往要考虑兼容性问题。由于你使用了XML 作为数据交换的格式,只需要提供XSLT 的模板就能将原有的数据格式自动转换成新版本的格式。除此以外,你可以将你的XML 表示的图形数据文件任意转换成其他标准的CAD 数据格式,一切都在你的掌握之中。
l          有较好的性能。
因为JAXB 在运行时在大部分时间中都是直接操纵内存中的Java 对象,只有在读取和存储文件的时候才会与XML 文件进行IO 操作,实质上是为XML 文件做了一个缓存,因此运行时的性能很好。
五. 使用JAXB的限制
跟直接使用Java 对象序列化相比,JAXB 也有它的限制。在使用Java 序列化的时候,Java 对象和XML 文档是直接对应起来的。例如,在画布上有一个 java.awt.Button 对象,那么当你进行序列化的时候, java.awt.Button 的所有数据被直接存储到序列化文件中;在进行图形恢复的时候,文件中的序列化数据直接就转换成 java.awt.Button 对象了。然而在使用JAXB 的时候,由于JAXB 没有将XML 文档转换成指定的Java 类的功能, XML 和应用程序中间会多一层。例如,你不能要求JAXB XML 文档数据中直接生成 java.awt.Button 对象,JAXB 在大多数环境中只能生成中间进行数据传递的对象。用 java.awt.Button 作为例子,使用JAXB ,你只能根据XML 中的数据生成JAXB 特有的 MyButton 对象,在再通过 MyButton 对象中的数据去初始化 java.awt.Button 对象。这种间接的数据传递所带来的负面影响是使用JAXB 的系统会产生大量传递数据的中间对象。虽然说,由于Java 虚拟机自动回收内存的特点,大量中间对象的产生有可能在长时间运行的Server 应用中降低系统的性能,但是这种数据传递的方法在许多系统设计中都是可行的。
六. 总结
本文通过一个简单的实例程序,演示了如何运用JAXB XML 设计开发你的应用系统,并且分析了JAXB 的优势和劣势。现在JAXB 越来越多的运用到各种应用程序当中,在越来越多的开放源码的产品都能看到JAXB 的影子。尤其是Web Services XML 的应用的迅速发展,JAXB 越来越受到广大Java 开发人员的重视。
七. 参考资料
1.         JAXB 开发包下载 http://java.sun.com/webservices/downloads/webservicespack.html
2.         JAXB 学习文档 http://java.sun.com/webservices/docs.html
3.         XML Schema 资料 http://www.w3.org/XML/Schema
4.         Java 对象序列化文档 http://java.sun.com/j2se/1.4.1/docs/guide/serialization/index.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值