[技术讲座] JAXB技术的魔力

类别:Java 点击:0 评论:0 推荐:
JAXB技术的魔力

                                                                              —— 二维CAD图象数据的存储


SUN中国软件技术中心      王昱     [email protected]

    摘要:本文介绍了怎样使用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的体系结构


图 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.zip)可以下载已经编译好的可单独运行的示例程序。

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包所带的“xjc”运行程序,例如

%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


    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主要是画布的绘图操作,包含了鼠标事件的响应和绘制屏幕的方法中。在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

 

本文地址:http://com.8s8s.com/it/it17845.htm