为了获得最佳的性能和内存使用,需要让数据绑定工具直接访问底层的 XML 流。AXIOM 完全允许这样做。延迟构建仅仅意味着只有在访问的时候才构造要访问的这部分树。因此如果不需要访问 SOAP 消息体,SOAP 消息的这部分就不会被构建。如果用户开始使用 SAX 或 StAX 访问消息体,而它还没有构建,AXIOM 将把用户直接连接到底层的解析器,以便提供最佳的性能。如图 2 所示:

图 2.通过 AXIOM 访问底层的解析器
但是,如果用户希望再回来访问树的同一部分就可能出现问题。因为解析器已经直接连接了用户,AXIOM 退出了,就是说所有信息都从低层的流直接流向用户。因此当用户回来请求同样的信息时,无论第二次选择什么样的 API,AXIOM 都不能提供该信息。注意这两种可能性差不多相等。比如,多数情况下 SOAP 体的处理中只有最终的服务实现才会涉及到负荷。服务可以使用数据绑定或其他 XML 处理 API 如 SAX、StAX 或 XPath 来处理消息体。这种情况下,消息体很少被访问两次,AXIOM 提供的优化具有最好的性能。
但是,假设在处理程序链中插入一个日志处理程序,使用 StAX writer 记录整个 SOAP 消息。如果服务实现尝试访问消息体,而消息体不存在!
为了进一步说明这一点,下面是一个比较简单的例子,虽然有点牵强。
StAXOMBuilder builder = new StAXOMBuilder(reader);
lineItem = builder.getDocumentElement();
lineItem.serialize(writer);
writer.flush();
price = lineItem.getFirstChildWithName(
new QName("http://openuri.org/easypo", "price"));
System.out.println("price= " + price.getText());
由于延迟构造,获得 lineItem 元素的时候该元素还没有构造完成。因此后面使用 StAX writer 进行序列化时,AXIOM 把 StAX writer(它序列化 lineItem 元素)直接连接到 StAX reader(它最初被传递给 builder)。但是这个过程中,AXIOM 断开了自身和数据流的连接。现在当请求 price 子元素的时候,找不到这样的元素,因为 lineItem 的所有子元素都在序列化器中消失了。
这种情况下,惟一的办法是避免序列化过程中 AXIOM 完全和数据流脱离开。用 AXIOM 的术语称为缓冲:无论是否在内存中建立了对象模型,AXIOM 都允许获得 StAX 事件或者 序列化 XML。因此,AXIOM 把策略(比如是否应该缓冲消息)和机制(如何缓冲)分离开来。它允许用户在开始使用原始 XML 处理 API(如 SAX 或 StAX)时决定是否缓冲树中未用到的部分以供将来引用。如果用户决定这样做,当树构造完成时可以再回来访问这些部分。但是,用户必须付出内存占用和性能的代价。另一方面,如果用户了解自己的目标,并确信只此一次需要访问树的这些部分,则可以选择关闭 缓冲来充分发挥 AXIOM 的效率。
因此,上一段代码应改写为:
StAXOMBuilder builder = new StAXOMBuilder(reader);
lineItem = builder.getDocumentElement();
lineItem.serializeWithCache(writer);
writer.flush();
price = lineItem.getFirstChildWithName(
new QName("http://openuri.org/easypo", "price"));
System.out.println("price= " + price.getText());
方法 serializeWithCache 与对应的 serialize 不同,不会将 StAX reader 直接连接到 StAX writer。相反,从 reader 传递给 writer 的所有数据都保留 在 AXIOM 中。具体如何缓冲与用户无关。目前如果启用缓冲,AXIOM 就会像用户在通过文档 API 访问树的这些部分一样构造树。
