zoukankan      html  css  js  c++  java
  • [转]JAXB技术的魔力

    作者: 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修饰符等等(详见参考资料)。

    lJDK版本的影响。
    不同的JDK的序列化实现有可能会有差异,保存数据的格式也有所不同,这就使得系统有可能被绑定在某个版本的JDK,而不能使用高版本的JDK所带来的性能上和功能上的好处!

    . XML数据表示和JAXB的解决方案

    由于存在以上的问题,需要考虑采用其他的解决方案,以保证即能够保证数据操作的灵活性,又能够使用现有的数据格式和成熟的开发工具包,减轻系统开发和维护的负担。

    XML成为被考虑的目标之一。显然,具有通用数据表示之称的XML能够带来很大的灵活性,使得数据在各个平台之间的交换,各个版本之间的升级变得非常方便;而且,在XML的开发中,Java平台已经拥有很多成熟的软件开发工具和解决方案。一般来说,使用XML作为系统数据存储的格式有以下两种解决方案:

    l直接使用Java对象序列化的内部对XML的支持功能:XmlEncoderXMLDecoder两个类。这个功能其实是对象序列化功能本身的一部分,通过使用XmlEncoderXMLDecoder这两个类,可以使序列化的数据以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处理模型如SAXDOMJAXB 隐藏了细节并且取消了SAXDOM中没用的关系——生成的JAXB类仅描述原始模型中定义的关系。其结果是结合了高度可移植Java代码和高度可移植的XML数据。其中这些代码可用来创建灵活、轻便的应用程序和Web服务。

    2.2. JAXB的体系结构

    1 JAXB体系结构
    JAXB的体系结构和应用过程如图1所示,一般来说包含以下几个步骤:

    l根据你的应用程序所要操作的XML数据格式,撰写相应的XML Schema,有关XML Schema的知识,请参阅参考资料

    l使用JAXB 所带的编译工具(Binding Compiler),将这个XML Schema文件作为输入,产生一系列相关的Java ClassInterface

    l在使用JAXB编译工具的时候,可以有选择性的提供一个配置文件(图1的虚线部分),来控制JAXB编译工具的一些高级属性。

    l这些Java ClassInterface是你的应用程序操纵XML数据的主要接口和方法。

    l通过JAXBXML文档进行的操作主要包括:将符合XML Schema规定的XML文档解析生成一组相应的Java对象;对这些对象进行操作(修改、增加和删除对象的属性等等);然后将这些对象的内容保存到这个XML文档中。

    下面我们结合本文的示例来进一步说明使用JAXB的过程。

    . 实例分析

    3.1. 示例运行的环境和步骤

    本示例运行的Java环境是JDK1.3.1以上。本示例在Jdk1.4.1运行测试通过。

    l这里(bin.zip)可以下载已经编译好的可单独运行的示例程序。

    l展开此压缩文件,在bin目录下找到可执行文件rundemo.batfor 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)菜单中的savesave as子菜单将当前已经绘制的图形以XML格式保存起来。

    l你还可以通过(File)菜单中的open子菜单,选择以前保存过的图形文件,将它显示在画布上。

    3.3. JAXB使用过程分析

    l下载JAXB开发工具包

     

     

    JAXB1.0 的正式版本的一个实现(大家一定要记住,JAXB只是一个标准,Sun公司提供的此工具包只能说是这个标准的一个实现)已经发布了。由于XMLWeb 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工具包的子目录下。

    udemo.xsd Schema的文件名,一般以xsd作为文件名的后缀。

    u-d 的选项,是指定系统生成的Java源代码所放置的目录

    u-p 的选项,是指定系统生成的Java源代码所在的Java Package的名称。

    u还有更多的选项,请参考JAXB的相关文档。

    如果运行成功的话,就会在你"-d"选项指定的目录下产生一些java代码。如果感兴趣的话,大家可以查看这些代码进行详细的研究。如果你就想知道怎样使用它们的话,那么只需要简单的了解就行了。

    l在应用程序中使用这些代码

     

     

    下面我们分析一下如何在我们的应用程序中使用JAXB工具包替我们生成的代码。在我们的实例中,主要有两个Java源文件:JaxbDemoFrame.javaMyCanvas.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;对于MarshallerUnmarshaller,这两个对象主要是用于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

    53public 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 中允许有一个或多个shape节点,那么在生成的ShapeContainer这个类中就getShape()方法来返回一个shape的集合。又比如,在Schema 中我们规定了每个shape都有一些属性(shapename, shapecolor, xposition, yposition)。那么在相应的ShapeType的类中,我们就会看到get和set的一些方法去设置和获得这些属性的值。

    . 使用XML和JAXB的优势

    l简单易用。
    通过上面的例子,大家可以发现所有对XML文档的操作都隐藏起来了,你的应用程序不用使用SAXDOM接口编程去操纵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和应用程序中间会多一层。例如,你不能要求JAXBXML文档数据中直接生成java.awt.Button对象,JAXB在大多数环境中只能生成中间进行数据传递的对象。用java.awt.Button作为例子,使用JAXB,你只能根据XML中的数据生成JAXB特有的MyButton对象,在再通过MyButton对象中的数据去初始化java.awt.Button对象。这种间接的数据传递所带来的负面影响是使用JAXB的系统会产生大量传递数据的中间对象。虽然说,由于Java虚拟机自动回收内存的特点,大量中间对象的产生有可能在长时间运行的Server应用中降低系统的性能,但是这种数据传递的方法在许多系统设计中都是可行的。

    . 总结

    本文通过一个简单的实例程序,演示了如何运用JAXBXML设计开发你的应用系统,并且分析了JAXB的优势和劣势。现在JAXB越来越多的运用到各种应用程序当中,在越来越多的开放源码的产品都能看到JAXB的影子。尤其是Web ServicesXML的应用的迅速发展,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

     

  • 相关阅读:
    requests
    Unit5 Going places
    Unit1 A time to remember
    SQL:找到一个关于all some any的用法,可在SSMS里看效果
    SQL join小结
    mac 配置tomcat
    oc与java c++语法区别
    swift调用oc项目
    java网络编程之socket
    windows远程控制mac
  • 原文地址:https://www.cnblogs.com/redcoatjk/p/3562355.html
Copyright © 2011-2022 走看看