清单 3. 代表顾客的 XML 文档
<?xml version="1.0" encoding="UTF-8"?>
<customer username="jimmy66">
<realname>James Hyrax</realname>
<orders>
<order id="o-11123" cost="$349.98">
<date>08-26-2005</date>
<items>
<item id="i-55768">
<name>Oolong 512MB CF Card</name>
<description>512 Megabyte Type 1 CompactFlash card.
Manufactured by Oolong Industries</description>
<price>$49.99</price>
</item>
<item id="i-74491">
<name>Fujak Superpix72 Camera</name>
<description>7.2 Megapixel digital camera featuring six
shooting modes and 3x optical zoom. Silver.</description>
<price>$299.99</price>
</item>
</items>
</order>
</orders>
</customer>
自行序列化的不足
有趣的是,清单 3 中的代码展示了让 JavaBean 把自己序列化为 XML 的一个主要不足。假设要用这个文档表示顾客的订单历史视图。在这种情况下,不太可能要显示每个历史订单中每个商品的完整说明,或者告诉顾客他或她自己的姓名。但是如果应用程序有一个 ProductSearch 类,它就是以 Item bean 列表的形式返回搜索结果,那么在 Item 的 XML 表示中包含说明可能会有帮助。而且,Item 类上代表当前库存水平的额外字段,在产品搜索视图中可能就是需要显示的有用信息。但是,不管当前的库存水平是否与当前情况相关(比如对顾客的订单历史来说),这个字段都会从包含 Item 的任何对象图中序列化出来。
从设计的角度来看,这是数据模型与视图生成耦合的经典问题。每个 bean 只能用一种途径序列化自己,一成不变的方式意味着 Ajax 交互最终要交换它们不需要交换的数据,因此造成客户端代码要从文档中找到需要的信息更加困难,而且也会增加带宽消耗和客户端的 XML 解析时间。这种耦合的另一个后果就是 XML 的语法不能脱离 Java 类独立变化。例如,对顾客文档的方案做修改,可能会影响多个 Java 类,造成它们也不得不做修改和重新编译。
我稍后会解决这些问题,但是首先来看一个对自行序列化方式的可伸缩性问题的解决方案:XML 绑定框架。
XML 绑定框架
近些年来,已经开发了多个 Java API 来简化 XML 文档到 Java 对象图的绑定过程。多数都提供了 XML 编排和拆解;也就是说,它们可以在 Java 对象图和 XML 之间执行双向会话。这些框架封装了 XML 处理的全部工作,这意味着应用程序代码只需要处理普通的 Java 类。它们还希望提供有用的辅助功能,例如文档验证。笼统来说,这些框架采用了两种不同的方式:代码生成和对象到 XML 映射。我将分别解释这两种方式。
代码生成方式
使用代码生成的框架包括 XMLBeans、JAXB、Zeus 和 JBind。Castor 也能使用这项技术。这类框架的起点是描述文档数据类型的 XML 方案。使用框架提供的工具,就可以生成代表这些方案定义类型的 Java 类。最后,用这些生成的类编写应用程序,表示自己的模型数据,并通过框架提供的一些辅助机制把数据序列化成 XML。
如果应用程序要使用大型 XML 语法,那么代码生成方式是个很好的方法。在数十个类上编写定制 XML 序列化代码的可伸缩性问题由此消除。另一方面,也不再需要定义自己的 JavaBean。框架生成的 Java 类通常非常符合 XML 的结构,所以对它们进行编码很难。而且,生成的类变成哑数据容器,因为一般不能向它们添加行为。一般来说,在应用程序代码中要做些妥协,才能很好地处理方案生成的类型。另一个缺陷是如果修改方案,会造成生成的类也要修改,所以也就会对围绕它们编写的代码带来相应的影响。
这种类型的 XML 绑定框架在数据拆解时最有用(例如,使用 XML 文档并把它们转化成 Java 对象)。除非拥有大型数据模型而且有可能从生成的类中获益,否则基于代码生成的框架对于 Ajax 应用程序来说可能有很大的杀伤力。
