分享

实用数据绑定: JaxMe——这一领域的新手

 shaobin0604@163.com 2007-01-23

2004 年 7 月 01 日

本 专栏的上一期全面概括了 JAXB,为您学习不同的数据绑定实现作好了准备。本文开始深入考察 JaxMe,它是 JAXB 的一种开放源代码实现。除了对基本 JAXB 规范作了一些改进之外,JaxMe 还集成了数据库和 Enterprise JavaBeans,这是对基本数据绑定行为的重要扩展。您可以通过 “XML 和 Java 技术”讨论论坛与作者和其他读者交流您对本文的看法。(您也可以单击本文顶端和底端的 讨论来访问论坛。)

SUN 的 JAXB,即 Java API for XML,曾经受到广泛的批评,这并不是一个秘密。在早期的版本(beta 或者其他某个版本)中,它是基于 DTD 的,完全不支持 W3C XML Schema。这种状态持续了一年多,然后,JAXB 突然发布了 1.0 版。虽然这个版本解决了模式的支持问题,却完全抛弃了 DTD,开发人员编写的代码(当然是对 beta 软件)突然不能使用了。近来,JAXB 得到了一些支持,但它仍然是一个非常封闭的环境,并在实现中留下许多很深的隐患。

本文将介绍的 JaxMe 项目保留了 JAXB 的许多好的特性,并克服了它的很多不足。首先 JaxMe 是源代码开放的(在 Apache 的大伞之下),这意味着即使它现在就消失了,开发人员也仍然能够使用甚至修改它的源代码,从而保证依赖于它的那些代码仍然能够很好地运行。如果说这一点还 不够,JaxMe 还提供了数据库交互、对 Enterprise JavaBeans 的支持等很多好的特性。

为了帮助您使用 JaxMe,我将详细地介绍类生成的方法。虽然这只是一项非常基本的任务,但可以帮助您熟悉 JaxMe,从而可以了解那些更有趣的特性。。

基本设置

设置 JaxMe 非常轻松。请访问 JaxMe 项目站点(请参阅 参考资料链接),并从 Apache 镜像站点下载其二进制形式。在撰写本文的时候,我下载的文件是 incubated-jaxme-0.2-bin.tar.gz。(在发布本文之前刚刚出现了 0.3 版,方法一样,只不过文件名改成了 incubated-jaxme-0.3-bin.tar.gz)。在您的开发机器上对该文件进行解压缩。虽然可以在命令行中使用 JaxME,但是这样做太痛苦了(因为有 太多的JAR 文件),本文使用 Ant 来处理 JaxMe 任务。强烈建议您也这样做,这里包括所有相关的 Ant 文件,而且很容易根据需要进行修改。





回页首


类生成

和 Jaxb 一样,在用 JaxMe 做某些工作之前需要一些 XML。清单 1 是一个非常简单的 XML 模式,它定义了一个学生。当然这里的定义很不完善,但是为了把精力集中到 JaxMe 而不是模式的语义上,有必要保持示例的简单性。



清单 1. 简单的学生模式
<?xml version="1.0" encoding="UTF-8"?>
                    <schema
                    xmlns="http://www./2001/XMLSchema"
                    xml:lang="EN"
                    targetNamespace="http://dw.ibm.com/jaxme/student"
                    xmlns:stu="http://dw.ibm.com/jaxme/student"
                    elementFormDefault="qualified"
                    attributeFormDefault="unqualified"
                    >
                    <element name="Student">
                    <complexType>
                    <sequence>
                    <element name="firstName" type="string" />
                    <element name="lastName" type="string" />
                    <element name="address" type="stu:Address" />
                    </sequence>
                    </complexType>
                    </element>
                    <complexType name="Address">
                    <sequence>
                    <element name="name" type="string"/>
                    <element name="street" type="string"/>
                    <element name="city" type="string"/>
                    <element name="state" type="string"/>
                    <element name="zip" type="positiveInteger"/>
                    </sequence>
                    </complexType>
                    <complexType name="College">
                    <sequence>
                    <element name="name" type="string" />
                    <element name="address" type="stu:Address" />
                    </sequence>
                    </complexType>
                    </schema>
                    

如果已经正确地设置了 Ant 构建过程(本文的 最后还会详细加以说明),只需输入 ant generate 就可以从这个模式生成类。我将这些细节留在了文章的最后,以便在阅读本文的过程中您能够把精力放在 JaxMe 及其语义上,最后再专门看一看 Ant。实际上,我建议您先通读一遍本文,然后再逐段地测试代码。这样,在您实际输入代码的时候,就已经掌握了其中的概念,可以更快地解决其中遇到的问 题。

包规范

所有生成的类都将放在 targetNamespace 属性指定的包中。这种约定是 JaxMe 独有的,因此,您应该好好地理解它的工作原理。看一看作为该属性参数提供的 URI: http://dw.ibm.com/jaxme/student 。这个字符串将被 JaxMe 模式编译器转化成包的名称。首先去掉“http://”,然后 逆转URI 中的主机名部分(这里就是“dw.ibm.com”),得到“com.ibm.dw”。这看起来有点奇怪,但实际上是通常的打包机制,对于用于开发特定站点或者 Bean(特别是标签库的包),这非常合理。

最后,URI 的剩余部分用斜杠( / )分隔,并将它们附加到从主机名派生的包名的后面。因此对于清单 1 中的模式,完整的包就是 com.ibm.dw.jaxme.student 。这个模式生成的所有类都将放在这个包中。

XML 语义

除了为 JaxMe 提供信息之外, targetNamespace 属性对于 XML 还有一些特殊的意义。它告诉模式处理程序将所有创建的构造(如 Address 这样的复杂类型)都放在该名称空间中。这意味着您需要通过这个名称空间引用这些构造,如果名称空间很长,则应该定义一个前缀映射到该名称空间。

注意:如果不能理解上面那一句话,您可能需要在学习一下 XML,特别是如何将 XML Schema 用于 XML。有关的更多信息,请参阅文章后面的 参考资料(以及 developerWorks XML 专区的其他文章)。现在不妨继续读下去,请相信我,但是要成为一名 JaxMe 专家,就必须花一些时间完全理解名称空间。

清单 1 的模式中,引用是通过 stu 前缀实现的。通过这个前缀,您可以很容易定义类型,然后通过模式引用它们(使用名称空间前缀)。

还应该知道,JaxMe 大量使用 include 指令(该例中未使用)。特别是对于大型模式,您可以将这些定义分段放在不同的文件中,然后在最高层的模式中使用下面方式引用它们:

<!-- Include definitions from another XML Schema -->
                    <include schemaLocation="file:///dev/jaxme/supplemental/datatypes.xsd"/>
                    

XML 模式处理程序将信息转移给 JaxMe,而对这些文件完全不作区分,因此我们建议,如果需要就使用多个模式。





回页首


得到的类

生成类之后,花点时间熟悉一下生成的结果。虽然这些类与 JAXB 创建的构造非常类似,但也存在少量细微的差别。

Student.java

它表示一个 XML 元素(来自 清单 1),是类层次的基本结构。实际上,它只是一个简单的接口,扩展了 StudentType (由 JaxMe 生成)和 Element ,后者是 JaxMe 运行时 API 的一部分。当然,这个类(以及所有其他生成的类)都在 com.ibm.dw.jaxme.student 包中。

StudentType.java

JaxMe 中任何名为 XXXType (其中 XXX 是像“Student”或“Address”这样的名称)的对象都是从模式中派生的定义。清单 2 显示了这个类的源代码,其中的内容非常简单。



清单 2. 生成的 StudentType 代码
package com.ibm.dw.jaxme.student;
                    public interface StudentType {
                    public String getFirstName();
                    public void setFirstName(String pFirstName);
                    public String getLastName();
                    public void setLastName(String pLastName);
                    public AddressType getAddress();
                    public void setAddress(AddressType pAddress);
                    }
                    

所有的属性都有访问( getXXX() )和修改( setXXX() )方法。当然,无论是简单字符串类型和更加复杂的类型,如 Address ,这些类型都用类表示,后面带有“Type”,因此在生成的清单中会看到对 AddressType 的引用。

AddressType.java 和 CollegeType.java

现在您已经看到了 清单 2StudentType.java,这些代码非常简单,您可以自行加以分析。

ObjectFactory.java

该文件在很多方面是 JaxMe 和特定于域的类之间的桥梁,最重要的方法有:

  • newInstance() 在特定于域的上下文中创建一个新元素并返回它。
  • createStudentType() 新创建的顶级 Student 元素。

非常奇怪的是,任何 JaxMe 文档中都没有提到该文件,提供的示例中也没有使用它。我个人倒是发现了它的某些用处,但是不建议您依赖该文件。不需要它也能完成所有的工作,如果有意在文档中忽略它,那么很可能在将来的版本中该文件就不会再出现。

Configuration.xml

该 XML 文件处理从 XML 到 Java 代码(或者相反)的映射。它将元素和类、字段和属性等联系起来。清单 3 给出了该例中相当简单的映射文件。



清单 3. 样例代码的 Configuration.xml
<Configuration xmlns="http://ws./jaxme/namespaces/jaxme2/configuration">
                    <Manager validatorClass="com.ibm.dw.jaxme.student.impl.StudentTypeValidator"
                    qName="{http://dw.ibm.com/jaxme/student}Student"
                    marshallerClass="com.ibm.dw.jaxme.student.impl.StudentTypeSerializer"
                    handlerClass="com.ibm.dw.jaxme.student.impl.StudentHandler"
                    elementInterface="com.ibm.dw.jaxme.student.Student"
                    elementClass="com.ibm.dw.jaxme.student.impl.StudentImpl"/>
                    <Manager validatorClass="com.ibm.dw.jaxme.student.impl.AddressTypeValidator"
                    marshallerClass="com.ibm.dw.jaxme.student.impl.AddressTypeSerializer"
                    handlerClass="com.ibm.dw.jaxme.student.impl.AddressTypeHandler"
                    elementInterface="com.ibm.dw.jaxme.student.AddressType"
                    elementClass="com.ibm.dw.jaxme.student.impl.AddressTypeImpl"/>
                    <Manager validatorClass="com.ibm.dw.jaxme.student.impl.CollegeTypeValidator"
                    marshallerClass="com.ibm.dw.jaxme.student.impl.CollegeTypeSerializer"
                    handlerClass="com.ibm.dw.jaxme.student.impl.CollegeTypeHandler"
                    elementInterface="com.ibm.dw.jaxme.student.CollegeType"
                    elementClass="com.ibm.dw.jaxme.student.impl.CollegeTypeImpl"/>
                    </Configuration>
                    

目前,JaxMe 要求直接修改该文件来改变映射。最常见的变化是您可能希望用自己的类代替生成的类。当然,您必须(至少对 JaxMe 的当前版本而言)生成默认的类, 然后再修改该文件。同样值得注意的是,实际上这种行为没有得到支持,尽管映射支持这样做,但是没有经过很好的测试,也没有很好的文档说明。

提示:将来的文章中我可能还要详细地讨论这种行为。如果对此感兴趣,请给我发电子邮件或者上传对本文的反馈意见,让我知道!这是了解对特定主题的兴趣要求的最好办法。

jaxb.properties

这是标准的 JAXB 性质文件,当然也经过了 JaxMe 的修改。其中只有一行,告诉 JAXB 工厂类使用 JaxMe 的实现类,如下所示:

javax.xml.bind.context.factory=org.apache.ws.jaxme.impl.JAXBContextImpl
                    

对熟悉 JAXP 的读者而言,在实现方式上,这与 Xerces 要求 JAXP 体系结构完成其实现是完全相同的。

impl/*.java

impl 子目录中的各个源文件都是由 JaxMe 创建的接口的具体实现。一般来说不用关心这些文件,它们处理文档的 XML 加工过程,将文档转换成 Java 中的等价物。

如果您喜欢刨根问底,那么可以告诉您这些类都是 SAX 类,因为 JaxMe 使用 SAX 解析 XML。事实上,Type 类(间接地)实现了 SAX 的 org.xml.sax.ContentHandler 接口。在源代码中您还会看到 startDocument()characters() 这样的方法(都列在这里的话太长了)。

虽然您可能不想对这些类太费心思,但是对其有个基本的了解还是不错的。您将会在代码中使用它们(很快就会看到),您会发现理解这些类对于查错和调试很有帮助。

编译

观察这些类的最后一个步骤就是编译它们。这似乎是显而易见的事,但是您恐怕难以相信那些因缺乏编译而造成的问题,或者类路径问题(稍后还会提到)。因此在使用这些类之前一定要编译它们。同样,我发现使用 Ant 来完成这项工作最简单,因此我使用 ant compile 来完成这项任务。

对于喜欢苦差事(或者只是爱好输入 javac )的人,类路径中一定要包括 jaxme-api.jarjaxme.jarjaxme-api.jar包含 JAXB API,而 jaxme.jar 则包含 JaxMe 实现类。在输出的时候,应该将所有的输出都放在基目录和编译后的 impl 目录中。最后,您可能希望复制所用的支持文件: Configuration.xmljaxb.properties。在运行时导入这些文件,以便对它们进行编组和解组。





回页首


使用 Ant 自动处理

您 会发现没有什么好的替代构建工具。好的工具可以为您节约反复处理类路径问题的大量时间,也不用您去记住特定的命令行选项。本文中遇到的几个问题都可以使用 Ant 来自动处理。我想花一点让您看一看我的 Ant 文件,这样您也可以把 Ant 结合到您的构建环境中。我以后的文章中也可以略过这些细节(艺多不压身,是不是?)。

类生成

首先,您会希望使用 JaxMe 模式编译器/类生成工具,用 org.apache.ws.jaxme.generator.Generator 接口表示。因此,您完全可以使用 Ant 的 java 目标创建该接口的一个实例。但是,这样做有点麻烦——需要在实现中进行硬编码,而且必须将构建文件和实现的变化混在一起。您可以将实现类定义为属性,但是 这样仍然存在非常低层的编码混合问题。对于使用 Ant 的人来说,所幸的是 JaxMe 包括用于在构建文件中插入类生成的 Ant taskdef (任务定义),它可以处理所有这些细节问题。假设您有一个定制的任务定义,如清单 4 所示:



清单 4. JaxMe 的 Ant taskdef
<path id="classpath.schema-generator">
                    <pathelement location="${lib}/jaxme2.jar" />
                    <pathelement location="${lib}/jaxmejs.jar" />
                    <pathelement location="${lib}/jaxmexs.jar" />
                    <pathelement location="${lib}/jaxmeapi.jar" />
                    </path>
                    <taskdef name="xjc"
                    classname="org.apache.ws.jaxme.generator.XJCTask"
                    classpathref="classpath.schema-generator"
                    />
                    

只要将这个片段插入 Ant 构建文件,生成类就变得非常简单。您可以使用清单 5 中的 XML 完成这项工作。



清单 5. 生成类
  <target name="generate" depends="init">
                    <xjc schema="student.xsd"
                    target="${dir.generated}">
                    <produces includes="com/ibm/dw/jaxme/student/*.java" />
                    </xjc>
                    </target>
                    

编译

生成类之后,现在需要编译并复制支持文件。清单 6 负责这项任务,它甚至还处理了类路径问题。



清单 6. 编译类
  <path id="classpath.schema-compiler">
                    <pathelement location="${lib.jaxme}/jaxmeapi.jar" />
                    <pathelement location="${lib.jaxme}/jaxme2.jar" />
                    </path>
                    <target name="compile" depends="generate">
                    <javac srcdir="${dir.generated}"
                    destdir="${dir.build}">
                    <classpath refid="classpath.schema-compiler" />
                    </javac>
                    <!-- Copy over support files -->
                    <copy todir="${dir.build}">
                    <fileset dir="${dir.generated}">
                    <include name="**/jaxb.properties" />
                    <include name="**/Configuration.xml" />
                    </fileset>
                    </copy>
                    </target>
                    

清理

显然,如果能够清除所做的工作,然后重新开始新的工作常常是有益的。虽然这并非 JaxMe 特有的,但也值得看一看。通常的办法是通过一个称为 clean 的目标(target)进行清理,如清单 7 所示。



清单 7. 清理
  <target name="clean">
                    <delete dir="${dir.generated}" />
                    <delete dir="${dir.build}" />
                    </target>
                    

清单 8 是一个更大的 Ant 文件,它将这些成分集中到了一起。实际上这也是我一直在用的文件,该文件对于这里描述的所有内容都适用。



清单 8. JaxMe Ant taskdef
<?xml version="1.0" encoding="UTF-8"?>
                    <project basedir=".">
                    <property name="lib.jaxme" value="/Users/bmclaugh/dev/lib" />
                    <property name="dir.build" value="build" />
                    <property name="dir.generated" value="generated" />
                    <path id="classpath.schema-generator">
                    <pathelement location="${lib.jaxme}/jaxme2.jar" />
                    <pathelement location="${lib.jaxme}/jaxmejs.jar" />
                    <pathelement location="${lib.jaxme}/jaxmexs.jar" />
                    <pathelement location="${lib.jaxme}/jaxmeapi.jar" />
                    </path>
                    <path id="classpath.schema-compiler">
                    <pathelement location="${lib.jaxme}/jaxmeapi.jar" />
                    <pathelement location="${lib.jaxme}/jaxme2.jar" />
                    </path>
                    <taskdef name="xjc"
                    classname="org.apache.ws.jaxme.generator.XJCTask"
                    classpathref="classpath.schema-generator"
                    />
                    <target name="init">
                    <mkdir dir="${dir.generated}" />
                    <mkdir dir="${dir.build}" />
                    </target>
                    <target name="generate" depends="init">
                    <xjc schema="student.xsd"
                    target="${dir.generated}">
                    <produces includes="com/ibm/dw/jaxme/student/*.java" />
                    </xjc>
                    </target>
                    <target name="compile" depends="generate">
                    <javac srcdir="${dir.generated}"
                    destdir="${dir.build}">
                    <classpath refid="classpath.schema-compiler" />
                    </javac>
                    <!-- Copy over support files -->
                    <copy todir="${dir.build}">
                    <fileset dir="${dir.generated}">
                    <include name="**/jaxb.properties" />
                    <include name="**/Configuration.xml" />
                    </fileset>
                    </copy>
                    </target>
                    <target name="clean">
                    <delete dir="${dir.generated}" />
                    <delete dir="${dir.build}" />
                    </target>
                    </project>
                    

您需要改变 lib.jaxme 属性的值,然后就可以了。该例中只需要输入 ant generate 就可以从模式生成类。您应该把这类工具(以及 Ant)放在手边,因为它使得编译和处理微妙的类路径变得非常简单。





回页首


结束语

只要充分理解了 JaxMe 处理类生成的方式,就可以很容易的实现与 XML 的相互转化。我将在下一篇文章讨论这个问题,然后再介绍 JaxMe 某些专有的特性,比如使用数据库。在这之前先用映射和 Configuration.xml来打发时间吧,但愿您过得愉快(也需要发一两次火)并真正掌握 JaxMe 的工作原理。在这期间,我将开始撰写下一期的稿子,那时再见吧。






回页首


下载

名字大小下载方法
x-pracdb4-code.zip 2KB  FTP|HTTP
关于下载方法的信息 Get Adobe? Reader?


参考资料



关于作者

 

Brett McLaughlin 从 Logo 时代(还记得那个小三角吗)就开始从事计算机。他目前专门使用 Java 相关技术构建应用程序基础设施。最近几年,他为 Nextel Communications 和 Allegiance Telecom, Inc. 实现了这些基础设施。Brett 是 Java Apache 项目 Turbine 的缔造者之一,该项目使用 Java servlet 为 Web 应用程序开发建立了可重用的组件体系结构。他还参与了 EJBoss 项目(一种开放源代码的 EJB 应用程序服务器)和 Cocoon(一种开放源代码的 XML Web 发布引擎)。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多