程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java Web服務,第2部分: 深度探索Axis2:AXIOM

Java Web服務,第2部分: 深度探索Axis2:AXIOM

編輯:關於JAVA

為什麼需要另一種文檔模型?

Apache Axis2 1.1 已經發布,它為那些長期運行 Apache Web 服務框架系列的忠實用戶提供了令人興奮的新特性。我們將在後續的文章中討論關於 Axis2 的內容,本文將深入研究 AXIs 對象模型 (AXIOM) XML 文檔模型,這是 Axis2 的核心。AXIOM 是 Axis2 中一個主要的創新,並且是 Axis2 能夠比原來的 Axis 提供更好性能的原因之一。本文將向您介紹 AXIOM 的工作原理、Axis2 的各個部分如何構建於 AXIOM 之上,以及 AXIOM 與其他的 Java™ 文檔對象模型的性能比較。

文檔模型通常用於對 XML 進行處理,並且在 Java 開發中有許多不同的文檔模型可供使用,包括原始 W3C DOM 規范的各種實現、JDOM、dom4j、XOM 等等。每種模型都聲稱與其他模型相比具有某些優點,要麼是在性能、靈活性方面,要麼是在嚴格遵守 XML 標准的程度方面,並且每種模型都擁有忠實的支持者。那麼,Axis2 為什麼需要一種新的模型呢?答案在於 SOAP 消息的結構,尤其是如何在基本的 SOAP 框架中添加相應的擴展。

SOAP 簡介

SOAP 本身僅僅只是 XML 應用程序負載的簡單包裝。清單 1 提供了一個示例,其中只有那些具有 soapenv 前綴的元素才真正是 SOAP 中定義的。文檔中大部分是應用程序數據,這些數據組成了 soapenv:Body 元素的內容。

清單 1. SOAP 示例

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
 <soapenv:Header/>
 <soapenv:Body>
  <matchQuakes xmlns="http://seismic.sosnoski.com/types">
   <min-date>2001-01-06T11:10:43.446Z</min-date>
   <max-date>2001-10-24T19:49:13.812Z</max-date>
   <min-long>-150.94307</min-long>
   <max-long>-22.594208</max-long>
   <min-lat>-11.44651</min-lat>
   <max-lat>55.089058</max-lat>
  </matchQuakes>
 </soapenv:Body>
</soapenv:Envelope>

盡管基本的 SOAP 包裝非常簡單,但是通過使用稱為 Header 的可選組件,它提供了不受限制的擴展能力。Header 為添加各種各樣的元數據提供了合適的位置,這些元數據與應用程序數據在一起,不會被應用程序看到(可以 在 Header 中包括應用程序數據,但是這樣做並不是很合理,您應該將應用程序數據放在消息的正文部分)。構建於 SOAP 之上的擴展(如整個 WS-* 系列),可以使用 Header 實現相應的目標,而不會對應用程序造成任何影響。這允許將擴展作為外接程序使用,可以在部署時選擇某個應用程序所需的特定擴展功能,而無需在代碼中對其進行處理。

清單 2 顯示了與清單 1 SOAP 示例相同的應用程序數據,但其中包括 WS-Addressing 信息。盡管原始的 SOAP 消息只能用於 HTTP 傳輸(因為 HTTP 提供了雙向的連接,使得響應可以立即發送回客戶端),但清單 2 中的版本可以用於其他協議,因為 SOAP 請求消息中直接包括了響應元數據。在對清單 2 的消息進行處理的過程中,甚至可以進行存儲轉發操作,因為這些元數據同時提供了請求目標和響應目標信息。

清單 2. 使用 WS-Addressing 的 SOAP 示例

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
   xmlns:wsa="http://www.w3.org/2005/08/addressing">
  <soapenv:Header>
   <wsa:To>http://localhost:8800/axis2/services/SeisAxis2XBean</wsa:To>
   <wsa:ReplyTo>
    <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
   </wsa:ReplyTo>
   <wsa:MessageID>urn:uuid:97AE2B17231A8584D811537402403691</wsa:MessageID>
  </soapenv:Header>
  <soapenv:Body>
   <matchQuakes xmlns="http://seismic.sosnoski.com/types">
    <min-date>2000-03-28T13:13:08.953Z</min-date>
    <max-date>2001-03-11T02:26:54.283Z</max-date>
    <min-long>-81.532234</min-long>
    <max-long>65.25895</max-long>
    <min-lat>-14.234512</min-lat>
    <max-lat>57.174187</max-lat>
   </matchQuakes>
  </soapenv:Body>
</soapenv:Envelope>

文檔模型面臨的一個進退兩難的問題

因為 SOAP Header 的關鍵思想是允許在消息中添加任何元數據,所以 SOAP 框架必須能夠接受某些擴展需要添加的任何內容。一般來說,處理任意的 XML 的最簡單方法是使用某種形式的文檔模型。這正是文檔模型的任務所在,即不對該 XML 的形式進行任何假設,如實地表示 XML。

但是對於處理在不同應用程序之間進行數據交換時所使用的 XML 來說,文檔模型並不是一種非常高效的方法。通常,應用程序數據具有預定義的結構,並且大多數開發人員更傾向於以數據對象而不是原始的 XML 的形式對這些數據進行處理。數據對象和 XML 之間的轉換任務由數據綁定負責完成。與使用文檔模型相比,數據綁定為開發人員帶來了更大的方便,同時,它在性能和內存使用方面的效率也更高。

所以,大多數應用程序希望使用數據綁定來處理 SOAP 消息的應用程序負載,但是對於處理 Header 中的元數據,使用文檔模型的方法更合適。理想的方法是在 SOAP 框架中同時使用這兩種技術,但普通的文檔模型不支持這種用法。它們希望處理整個文檔,或者至少是文檔中一個完整的子樹。它們無法處理文檔中所選的部分,但這是處理 SOAP 的最合適的方式。

以拉方式構建 AXIOM 樹

除了 AXIOM 之外,Axis2 還對 Axis 進行了另一個改進。原始的 Axis 使用標准的推式 (SAX) 解析器進行 XML 處理,而 Axis2 使用拉式 (StAX) 解析器。在使用推方式的情況下,由解析器來控制解析操作,需要向解析器提供要解析的文檔和處理程序。然後,在對輸入文檔進行處理的時候,它使用代碼中的處理程序作為回調。處理程序代碼可以使用這些回調中傳遞的信息,但是不能影響解析過程(除了引發一個異常)。相反,在使用拉方式的情況下,解析器實際上是一個高效的迭代器,可以根據需要對文檔中的不同部分進行遍歷。

推方式和拉方式分別具有各自的用途,但是對於處理包含邏輯上獨立的不同部分的 XML(如 SOAP),使用拉方式更有優勢。。使用拉式解析器,處理文檔某一部分的代碼僅解析它所需的部分,然後由解析器進行接下來的文檔處理。

AXIOM 構建於 StAX 拉式解析器接口的基礎之上。AXIOM 提供了一種可以按需擴展的虛擬文檔模型,僅構建客戶端應用程序所請求的樹結構文檔模型表示。這種虛擬的文檔模型工作於 XML 文檔的元素級。當解析器報告元素開始標記時創建元素表示,但是該元素的初始形式僅僅只是一個殼,其中保存了對解析器的引用。如果應用程序需要獲取元素內容的細節信息,它只需要通過調用接口(如 org.apache.axiom.om.OMContainer.getChildren() 方法)的方法,就可以請求相應的信息。然後,在對這個方法調用的響應中,解析器將構建該元素的子內容。

因為解析器按照文檔順序(與 XML 文檔文本中項目的出現順序相同)傳遞數據,所以 AXIOM 所實現的按需構造需要某種靈活的處理方法。例如,通常有多個元素處於不完整的(正在構建的過程中)狀態,但是這些元素都必須位於繼承結構的一條直線中。如果從根元素在頂端的標准 XML 樹關系圖的角度來看,不完整的元素將始終位於該樹右側的直線中。隨著應用程序請求更多的數據,該樹逐漸向右擴充,並且首先完成頂端的元素。

使用 AXIOM

就所提供的 API 而言,所有的 XML 文檔模型都具有很多相同之處(這並不奇怪,因為它們都需要處理相同的基礎數據),但是與其他的文檔模型相比,它們又各有千秋。原始的 W3C 文檔對象模型 (DOM) 設計提供跨語言和跨平台的兼容性,所以構建於各種接口之上,並且在其自身的版本中避免使用 Java 特定的集合。JDOM 使用了具體類而不是接口,並且在這種 API 中集成了標准的 Java 集合類,許多 Java 開發人員認為它比 DOM 更友好。dom4j 將類似 DOM 的接口和 Java 集合類組合到一起,這種非常靈活的 API 提供了很多強大的功能,但卻在一定的程度上增加了復雜性。

AXIOM 與其他文檔模型有許多相似之處。它還具有一些與其按需構建的處理過程相關的顯著特征,以及支持其用於 Web 服務的專門特性。

實際應用中的 AXIOM

從整體上看,AXIOM 的 API 可能最接近於 DOM,但是它又具有自己的特點。例如,訪問方法以使用 java.util.Iterator 實例為基礎對不同的部分(如通過 org.apache.axiom.om.OMContainer.getChildren() 以及相關方法所返回的部分)進行訪問,而沒有使用任何形式的列表。沒有將不同的部分索引為列表,導航使用 org.apache.axiom.om.OMNode.getNextOMSibling() 和 org.apache.axiom.om.OMNode.getPreviousOMSibling() 方法,以便按順序遍歷文檔樹中同一個級別的節點(在這方面,它類似於 DOM)。這種訪問和導航方法的結構與按需構建樹的工作方式非常吻合,因為這意味著 AXIOM 可以允許您移動到開始元素的第一個子項,而無需首先處理所有的 子元素。

與 DOM 和 dom4j 一樣,AXIOM 使用接口定義了用於訪問和操作樹表示的 API。AXIOM 分發版中包括這些接口的幾種不同的專門實現。其中一種實現(在 org.apache.axiom.om.impl.dom 包中)是雙重功能的,使用相同的實現類同時支持 AXIOM 和 DOM 接口。因為需要使用數據的 DOM 視圖的 Web 服務外接程序的數目較多,所以這種實現可能很有價值。有關更一般的用法,org.apache.axiom.om.impl.llom 包提供了一種基於對象鏈表的實現(包名中的“ll”表示鏈表 (linked list))。還對基本的 org.apache.axiom.om 接口和 org.apache.axiom.soap 包樹中的實現進行了擴展,後者專門用於處理 SOAP 消息。

為了簡要地了解實際應用中的 AXIOM API,我們將對一些示例進行研究,這些示例來自於對 AXIOM 與其他的文檔模型進行性能測試對比的代碼。清單 3 中提供了第一個示例,這個示例基於為輸入文檔構建 AXIOM 表示的代碼。與使用 DOM 和 dom4j 一樣,在使用 AXIOM 進行任何工作之前,您需要獲得一個構建模型組件對象的工廠。通過使用 org.apache.axiom.org.OMFactory 接口的 org.apache.axiom.om.impl.llom.factory.OMLinkedListImplFactory 實現,清單 3 中的代碼選擇了 AXIOM 接口的基本鏈表實現。這個工廠接口包括用於從各種來源直接構建文檔的方法、用於創建 XML 文檔表示的不同部分的方法。清單 3 使用了相應的方法從輸入流構建文檔。build() 方法返回的對象實際上是 org.apache.axiom.om.OMDocument 的實例,盡管在這段代碼中沒有指明。

清單 3. 將文檔解析為 AXIOM

import org.apache.axiom.om.*;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axiom.om.impl.llom.factory.OMLinkedListImplFactory;
   ...
   private XMLInputFactory m_parserFactory = XMLInputFactory.newInstance();
   private OMFactory m_factory = new OMLinkedListImplFactory();
   ...
   protected Object build(InputStream in) {
     Object doc = null;
     try {
       XMLStreamReader reader = m_parserFactory.createXMLStreamReader(in);
       StAXOMBuilder builder = new StAXOMBuilder(m_axiomFactory, reader);
       doc = builder.getDocument();
     } catch (Exception ex) {
       ex.printStackTrace(System.out);
       System.exit(0);
     }
     return doc;
   }

清單 3 使用了 org.apache.axiom.om.impl.builder.StAXOMBuilder 類通過解析輸入流來構建文檔表示。這段代碼在返回之前僅創建了 StAX 解析器實例和基本文檔結構,並使得解析器定位到文檔的根元素,如果需要,稍後再構建文檔表示的其他部分。並不一定 必須使用 StAX 解析器來構建 AXIOM。事實上,org.apache.axiom.om.impl.builder.SAXOMBuilder 是基於 SAX 推式解析器的構建程序的部分實現。但如果您使用其他方法來構建它,那麼就無法利用按需構造所帶來的優點。

清單 4 提供了用於遍歷文檔表示中的元素並累計摘要信息(元素的計數,屬性值文本的計數和總長度,以及文本內容的計數和總長度)的代碼。底部的 walk() 方法接受需要進行匯總的文檔以及摘要數據結構作為參數,而頂部的 walkElement() 方法則處理一個元素(遞歸地調用自己以便對子元素進行處理)。

清單 4. 導航 AXIOM

/**
    * Walk subtree for element. This recursively walks through the document
    * nodes under an element, accumulating summary information.
    *
    * @param element element to be walked
    * @param summary document summary information
    */
   protected void walkElement(OMElement element, DocumentSummary summary) {

     // include attribute values in summary
     for (Iterator iter = element.getAllAttributes(); iter.hasNext();) {
       OMAttribute attr = (OMAttribute)iter.next();
       summary.addAttribute(attr.getAttributeValue().length());
     }

     // loop through children
     for (Iterator iter = element.getChildren(); iter.hasNext();) {

       // handle child by type 
       OMNode child = (OMNode)iter.next();
       int type = child.getType();
       if (type == OMNode.TEXT_NODE) {
         summary.addContent(((OMText)child).getText().length());
       } else if (type == OMNode.ELEMENT_NODE) {
         summary.addElements(1);
         walkElement((OMElement)child, summary);
       }

     }
   }

   /**
    * Walk and summarize document. This method walks through the nodes
    * of the document, accumulating summary information.
    *
    * @param doc document representation to be walked
    * @param summary output document summary information
    */
   protected void walk(Object doc, DocumentSummary summary) {
     summary.addElements(1);
     walkElement(((OMDocument)doc).getOMDocumentElement(), summary);
   }

最後,清單 5 給出了用於將該文檔表示轉換為輸出流的代碼。作為 OMNode 接口的一部分,AXIOM 定義了許多輸出方法,包括針對各種不同的目標(如輸出流、普通字符寫入程序、或 StAX 流寫入程序),帶和不帶格式信息,帶和不帶在寫入之後訪問文檔表示的功能(如果尚未構建,這需要構建完整的表示)。通過從元素中獲得 StAX 解析器,OMElement 接口定義了另一種訪問文檔信息的方法。這種使用解析器從文檔表示中提取 XML 的能力提供了很好的對稱性,當按需構建 AXIOM 表示時,這種方式非常合適(因為可以直接返回用於構建該表示的解析器)。

清單 5. 從 AXIOM 寫入文檔

/**
    * Output a document as XML text.
    *
    * @param doc document representation to be output
    * @param out XML document output stream
    */
   protected void output(Object doc, OutputStream out) {
     try {
       ((OMDocument)doc).serializeAndConsume(out);
     } catch (Exception ex) {
       ex.printStackTrace(System.err);
       System.exit(0);
     }
   }

AXIOM 為修改現有的文檔部分提供了一些基本的方法(如 OMElement.setText() 可用於將元素的內容設置為一個文本值)。如果您正從頭開始寫入文檔,那麼需要直接創建組件的新實例。因為 AXIOM API 是基於接口的,所以它使用各種工廠來創建不同部分的具體實現。

有關 AXIOM API 的更詳細信息,請參見參考資料部分提供的 AXIOM 教程和 JavaDoc 鏈接。

AXIOM 中的 MTOM

AXIOM 的最有趣的特性之一是,它對 SOAP 附件最新版本中所使用的 W3C XOP 和 MTOM 標准提供了內置的支持。這兩種標准協同工作,其中 XOP(XML 二進制優化打包,XML-binary Optimized Packaging)提供了一種方式,使得 XML 文檔在邏輯上可以包含任意二進制數據的大對象,而 MTOM(SOAP 消息傳輸優化機制,SOAP Message Transmission Optimization Mechanism)則將 XOP 技術應用於 SOAP 消息。XOP 和 MTOM 都是新一代 Web 服務框架的關鍵特性,因為它們最終提供了可互操作的附件支持以及解決本領域中當前問題的能力。

XOP 使用 Base64 編碼的字符數據內容。通過對原始數據中的每六位使用一個 ASCII 字符來表示,Base64 編碼可以將任意的數據值轉換為可打印的 ASCII 字符。因為二進制數據通常不能嵌入到 XML 中(XML 只能處理字符,不能處理原始的字節,甚至有一些字符編碼也不允許出現在 XML 中),Base64 編碼非常適合於在 XML 消息中嵌入二進制數據。

XOP 使用 XOP 命名空間中特殊的“Include”元素來替換 Base64 文本。Include 元素給出了標識單獨實體(在該 XML 文檔之外)的 URI,該實體是希望包括在 XML 文檔中的實際數據。通常,這個 URI 將在與 XML 文檔相同的傳輸中標識一個單獨的塊(盡管可以不這樣做,但這樣提供的潛在優勢可通過中間層或存儲文檔來交換文檔)。使用對原始數據的引用來替換 Base64 文本,可以在一定程度上減小文檔大小(對於普通字符編碼,最多可以減少百分之二十五的大小),並實現更快的處理速度,而無需承擔對 Base64 數據進行編碼和解碼所帶來的開銷。

MTOM 構建於 XOP 之上,首先定義了一個抽象的模型,表示如何將 XOP 用於 SOAP 消息,然後對該模型進行特殊化使其專門用於 MIME Multipart/Related 打包,最後將其應用於 HTTP 傳輸。通過這些處理,它提供了一種標准的方式,通過廣泛使用的 HTTP 傳輸將 XOP 應用於 SOAP 消息。

AXIOM 通過 org.apache.AXIOM.om.OMText 接口以及該接口的實現來支持 XOP/MTOM。OMText 定義了相應的方法以支持代表二進制數據的文本項目(以 javax.activation.DataHandler 的形式表示,這是 Java Web 服務框架中用於附件支持的廣泛使用的 Java Activation API 的一部分),以及一個“優化”標志用來表示是否可以使用 XOP 對該項目進行處理。org.apache.AXIOM.om.impl.llom.OMTextImpl 實現添加了一個與 MTOM 兼容的內容 ID,在創建或自動生成類的實例時,如果尚未設置,可以對其進行相應的設置。

清單 6 通過一個示例介紹了如何在 AXIOM 中使用 XOP/MTOM 來構建消息。這段代碼取自一個性能測試示例,該示例使用 Java 序列化將結果數據結構轉換為一個字節數組,然後返回這個數組作為附件。

清單 6. 創建 XOP/MTOM 消息

public OMElement matchQuakes(OMElement req) {
     Query query = new Query();
     Iterator iter = req.getChildElements();
     try {
       ...
       // retrieve the matching quakes
       Response response = QuakeBase.getInstance().handleQuery(query);

       // serialize response to byte array
       ByteArrayOutputStream bos = new ByteArrayOutputStream();
       ObjectOutputStream oos = new ObjectOutputStream(bos);
       oos.writeObject(response);
       byte[]byts = bos.toByteArray();

       // generate response structure with reference to data 
       ByteArrayDataSource ds = new ByteArrayDataSource(byts);
       OMFactory fac = OMAbstractFactory.getOMFactory();
       OMNamespace ns =
         fac.createOMNamespace("http://seismic.sosnoski.com/types", "qk");
       OMElement resp = fac.createOMElement("response", ns);
       OMText data = fac.createOMText(new DataHandler(ds), true);
       resp.addChild(data);
       return resp;

     } catch (ParseException e) {
       e.printStackTrace();
     } catch (IOException e) {
       e.printStackTrace();
     }
     return null;
   }

盡管清單 6 中的代碼生成了可以使用 XOP/MTOM 發送的響應,但在 Axis2 的當前版本中,在缺省情況下 XOP/MTOM 支持處於禁用狀態。要啟用它,需要在 Axis2 axis2.xml 文件或服務的 services.xml 文件中包括參數 <parameter name="enableMTOM">true</parameter>。作為即將介紹的性能比較中的一部分,我們將提供這個示例的完整代碼,但現在我們將以一個實際使用 XOP/MTOM 的示例作為結束。

清單 7 顯示了由清單 6 的服務所生成的響應消息的結構,啟用或沒有啟用 XOP/MTOM(在第一個示例中不包含 MIME Header 和實際的二進制附件,而在第二個示例中刪除了大部分的數據)。

清單 7. 帶和不帶 XOP/MTOM 的響應消息

<?xml version='1.0' encoding='UTF-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header />
   <soapenv:Body>
    <qk:response xmlns:qk="http://seismic.sosnoski.com/types"
      xmlns:tns="http://ws.apache.org/axis2">
     <xop:Include href="cid:1.urn:uuid:[email protected]"
       xmlns:xop="http://www.w3.org/2004/08/xop/include" />
    </qk:response>
   </soapenv:Body>
  </soapenv:Envelope>
[actual binary data follows as separate MIME part with referenced content id]

<?xml version='1.0' encoding='UTF-8'?>
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header />
   <soapenv:Body>
    <qk:response xmlns:qk="http://seismic.sosnoski.com/types"
      xmlns:tns="http://ws.apache.org/axis2">rO0ABXNyACdjb20uc29zb...</qk:response>
   </soapenv:Body>
  </soapenv:Envelope>

數據綁定掛鉤

大多數 Web 服務開發人員需要以 Java 對象而不是 XML 文檔(或者甚至文檔模型,如 AXIOM)的形式使用數據。上一代框架,如 Axis 和 JAX-RPC,實現了自己的數據綁定,以便在 XML 和 Java 對象之間進行轉換,但這是一種非常有限的解決方案。Web 服務框架中的數據綁定實現通常無法與專門的數據綁定框架相比,所以希望更好地控制 XML 處理過程的用戶不得不將該框架與低效的轉換代碼結合在一起。因為這些問題,Axis2 重新進行了設計,以支持使用各種數據綁定框架作為數據綁定“插件”。

這種數據綁定支持使用對 Axis2 中包含的 WSDL2Java 工具的自定義擴展。該工具基於 WSDL 服務描述為客戶端或服務器端消息接收者以存根的形式生成 Axis2 連接代碼。客戶端存根可作為代理進行服務的調用,它定義了實現服務操作的方法調用。服務器端消息接收者可作為客戶端的代理,調用實際的用戶定義的服務方法。當在 WSDL2Java 命令行中請求數據綁定時,該工具將調用指定的數據綁定框架擴展,在存根或消息接收者中生成在 OMElement 和 Java 對象之間進行轉換的代碼。在使用存根的情況下,在方法調用中傳遞 Java 對象(或原始值),並將經過轉換的 XML 作為請求發送到服務器。然後將返回的 XML 重新轉換為 Java 對象,再將後者作為方法調用的結果返回。服務器端的消息接收者以相反的方式進行轉換。

在入站或分解端(將接收到的 XML 轉換為 Java 對象),處理過程比較簡單。需要轉換為 Java 對象的 XML 文檔負載可以通過 OMElement 的形式獲得,並且數據綁定框架只需要處理該元素中的數據。OMElement 以 StAX javax.xml.stream.XMLStreamReader 的形式提供了對元素數據的訪問,當前大多數的數據綁定框架可以直接使用 javax.xml.stream.XMLStreamReader 作為輸入。

出站或封送端(將 Java 對象轉換為傳輸的 XML)的處理稍微復雜一些。AXIOM 的關鍵思想是,除非在絕對必要的情況下,否則將避免構建完整的 XML 數據表示。要在封送時支持這個原則,必須有一種方式使得可以僅在需要的時候調用數據綁定框架。AXIOM 通過使用 org.apache.AXIOM.om.OMDataSource 作為數據綁定轉換的包裝來處理這個問題。OMDataSource 定義了相應的方法,以便使用 AXIOM 所支持的任何方法寫出包裝的內容(到 java.io.OutputStream、java.io.Writer 或 StAX javax.xml.stream.XMLStreamWriter),以及為包裝的內容返回解析器實例的另一種方法。OMElement 實現可以使用 OMDataSource 的實例按需提供數據,而 OMFactory 接口提供了創建這種類型的元素的方法。

性能

我們將對性能進行簡要的分析,以此總結關於 AXIOM 的內容。在撰寫這篇文章的時候,AXIOM 1.1 已經發布,這意味著本文中描述的接口應該比較穩定。另一方面,隨著實際實現代碼的修改,性能總在發生著變化。與其他的文檔模型相比,我們並不期望 AXIOM 在性能上有什麼重大的改進,但是其中的一些具體細節可能有些差異。

為了將 AXIOM 與其他的文檔模型進行比較,我們對以前的文檔模型研究(請參見參考資料部分)中的代碼進行了更新,添加了 AXIOM 和另一種新的文檔模型 (XOM),對代碼進行轉換使其使用 StAX 解析器標准,而不是較早的 SAX 標准,並切換到使用 Java 5 中引入的改進的 System.nanoTime() 計時方法。性能測試代碼首先將測試中使用到的文檔讀入到內存中,然後對文檔進行一系列的操作。首先,使用解析器構建文檔表示的某個數量的副本,並保存每個結果文檔對象。接下來,對每個文檔對象進行遍歷,這意味著代碼將掃描整個文檔表示(包括所有的屬性、值和文本內容)。最後,將所有的文檔對象寫入到輸出流。對每項單獨的操作,記錄其時間,並在測試結束後計算其平均值。

因為 AXIOM(尤其是用於 Axis2 中時)主要關注於處理 SOAP 消息,所以我們使用了三個不同的 SOAP 消息測試用例來檢查性能。第一個測試用例是一個 Web 服務性能測試的示例響應,該服務提供了某個時間段和經/緯度范圍內的地震信息(“quakes”文檔,18 KB)。這個文檔包含許多重復的元素和一些嵌套的情況,並大量使用了屬性。第二個測試用例是來自 Microsoft WCF 可互操作性測試的一個較大的示例,由單個重復的結構組成,並且在取值上有一些細微的變化(“persons”文檔,202 KB)。這個文檔具有帶文本內容的子元素,但是沒有使用屬性。第三個測試用例是由 30 個較小的 SOAP 消息文檔組成的集合,這些消息來自於一些較早的可互操作性測試(總大小為 19 KB)。從測試文檔中刪除了所有用來進行格式化的空格,以建立真正的生產服務(通常關閉格式化,以減少消息的大小)交換使用的 XML 文檔表示。

下面的圖表顯示了對每個文檔進行 50 次處理所需的平均時間。測試環境為 Compaq 筆記本系統,1600 MHz ML-30 AMD Turion 處理器和 1.5 GB RAM,在 Mandriva 2006 Linux 上運行 Sun 的 1.5.0_07-b03 JVM。我們測試了 AXIOM 1.0、dom4j 1.6.1、JDOM 1.0、Xerces2 2.7.0 和 Nux 1.6 分發版中的 XOM。為 dom4j、JDOM 和 Xerces2 使用 StAX 解析器的自定義構建程序,而對 XOM 則使用 Nux StAX 解析器構建程序。所有的測試都使用了 Woodstox StAX 解析器 2.9.3。

圖 1 顯示了測試序列中前兩個步驟所需的平均時間的總和,即使用解析器構建文檔,並遍歷文檔表示以檢查其中的內容。如果單獨研究第一個步驟(這裡沒有顯示時間),AXIOM 要比其他的文檔模型好得多,至少對於前面兩個文檔來說是這樣的。然而,這僅表示 AXIOM 正如預料的那樣工作,直到需要時才真正構建完整的文檔。我們希望使用在內存中構建完整的表示所需的時間來進行公平的比較,這正是圖表中將這兩個時間加在一起的原因。

圖 1. 構建和展開文檔的時間(單位為毫秒)

如圖 1 所示,從整體上看,除了 Xerces2 之外,AXIOM 比所測試的任何其他文檔模型都要慢一些。

在處理較小的文檔組成的集合(“soaps”)時,有一些文檔模型出現了性能問題。Xerces2 在這種情況下最糟糕,但是 AXIOM 也出現了較大的開銷,這可能是該圖表中反映出來的最麻煩的問題。在許多 Web 服務中,較小的消息是很常見的,而 AXIOM 應該可以高效地處理這些消息。因為 AXIOM 設計為按需展開文檔樹,所以對於兩個較大的文檔,在時間上並不存在很大的問題,至少與其他的文檔模型非常接近。

圖 2. 寫入文檔的時間(單位為毫秒)

圖 2 顯示了使用每種模型將文檔寫入到輸出流中的平均時間。這裡,Xerces2 的性能要高出一大截(但這並不足以彌補它在構建步驟中糟糕的性能,這兩張圖表的比例並不一樣),而 AXIOM 最差。同樣的,AXIOM 對於較小的文檔尤其糟糕。

圖 3. 文檔內存大小(單位為 KB)

最後,圖 3 顯示了每種框架在表示文檔時所使用的內存大小。dom4j 又是最好的,而 AXIOM 則比其他的差了一大截。AXIOM 在內存使用方面的糟糕性能,在一定程度上是由於在所構建的文檔中引用了解析器,以便在使用文檔實例的過程中保存解析器實例。這也是 AXIOM 在處理較小文檔時尤其糟糕的原因中的一部分。然而,AXIOM 作為文檔組件所使用的對象也比其他文檔模型的對象大的多,這種差別可能正是 AXIOM 對兩個較大的測試文檔使用過多內存空間的原因(其中,固定大小的解析器開銷和其他數據結構只占總的內存使用中的一小部分)。

如果將前兩個圖表的時間累加起來,dom4j 的總體性能最好,而 Xerces2 的性能最差(AXIOM 比它稍微好一點)。在內存使用方面,dom4j 又是最好的,而 AXIOM 在這方面是毫無爭議的失敗者。是不是為 AXIOM 感到遺憾呢?

如果始終構建完整的樹表示,那才是應該 遺憾的,但請記住,AXIOM 的關鍵思想是在通常情況下並不需要完整的表示。圖 4 顯示了在 AXIOM 中初始文檔構建的時間,並與其他文檔模型構建文檔表示所需的時間進行了比較。在這個測試中,AXIOM 比其他的文檔模型快得多(對於兩個較大的文檔,甚至快得無法記錄時間)。同樣的比較也可以應用於內存方面。最後的結論是,如果只需要處理文檔模型中的某些部分(如按文檔順序的“第一”部分),AXIOM 提供了優秀的性能。

圖 4. 初始文檔構建

繼續學習 Axis2

本文深入研究了 Axis2 中的 AXIOM 文檔對象模型。AXIOM 中包含了一些有趣的思想,尤其是構建完整表示的按需構建方法。當您需要完整地展開表示時,它無法與其他的 Java 文檔模型相比。尤其是對於較小的文檔,其性能比較糟糕,但是它為 Web 服務處理所提供的靈活性完全可以抵銷這些問題。

現在,您已經了解了 Axis2 如何使用 AXIOM 處理 SOAP 消息表示,包括如何與數據綁定框架相互傳遞 XML。下一篇文章將從用戶的角度來研究 Axis2 如何支持不同的數據綁定框架,包括使用三種框架的代碼示例。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved