1. 程式人生 > >關於JAXP技術的幾點總結

關於JAXP技術的幾點總結

本文只是個人總結,不是結論,因此不保證文章內容的正確性,僅是自己學習過程中的一些個人理解而已...


JAXP簡介


JAXP全稱Java API for XML Processing,最開始的時候(JAXP1.0)是叫Java API for XML Parsing,因為那個時候JAXP還僅支援XML的解析,後來JAXP不斷進化,其支援的內容不斷增加,也就改名為Processing了。

JAXP利用標準解析器Simple API for XML Parsing (SAX) 和 Document Object Model (DOM) 使我們可以在將資料作為事件流來解析或者構建出文件物件模型來解析中作出選擇;JAXP 支援 Extensible Stylesheet Language Transformations (XSLT) 標準, 使我們能夠將資料轉換成其他的XML文件或其他格式,如HTML;從JAXP1.4版本開始,Streaming API for XML (StAX,JSR-173) 被加入到JAXP家庭中來。


JAXP與XML語法解析器


嚴格來說,JAXP 是 API,但是將其稱為抽象層更準確。它不提供處理 XML 的新方式,不補充 SAX 或 DOM,也不向 Java 和 XML 處理提供新功能。它只是使通過 DOM 和 SAX 處理一些困難任務更容易。如果在使用 DOM 和 SAX API 時遇到特定於供應商的任務,它還使通過獨立於供應商的方式處理這些任務成為可能。

需要牢記一點:JAXP 不提供語法分析功能 !使用JAXP時如果沒有 SAX、DOM 或另一個 XML 語法分析 API,就 無法分析 XML 語法。雖然JAXP不提供語法分析,但JAXP提供到達語法分析器的方式;JAXP可以看作是一套規範,它可以使用不同提供商的XML解析器,後面會介紹如果切換其它的XML解析器。

現在,JAXP在SAX和DOM解析方式中預設採用Apache的xerces專案中的XML語法分析器;而對於StAX解析方式則預設採用Sun自己提供的語法分析器。這些語法分析器不屬於JAXP API的一部分,如前面所述,JAXP不提供XML語法分析功能,這可能容易令人困惑。舉個例子來說,這與JDOM使用Apache的Xerces語法分析器,但Xerces並不屬於JDOM的一部分是一樣的意思。

明白了沒?JAXP API和XML語法分析器是相互獨立的。


JAXP相關API簡介

 不說明具體的API及各API如何使用,僅說明一下各解析方式都有哪些相關的API以及在JDK中是如何組織的,具體使用需要去查JDK提供的API文件。


相關API總覽

  • javax.xml.parsers: JAXP相關API,為不同SAX and DOM 解析提供商提供通用介面
  • org.w3c.dom: 文件物件模型(DOM)相關的介面
  • org.xml.sax: 定義基本的SAX相關API
  • avax.xml.transform:定義了XSLT相關 API,使我們可以將XML轉換成其它形式
  • javax.xml.stream:提供StAX相關的 API

SAX相關API


SAX解析API大綱圖:


SAX相關包概覽

  • org.xml.sax:定義了SAX相關介面
  • org.xml.sax.ext:定義了用於更復雜的SAX處理的擴充套件類,例如處理DTD或者檢視檔案詳細語法
  • org.xml.sax.helpers:包含了使SAX應用更簡單的幫助類,比如,定義包含空方法的DefaultHandler
  • javax.xml.parsers:定義了SAXParserFactory 類,用於返回SAXParser解析器;另外定義了用於報告解析錯誤的異常

DOM解析相關API


DOM解析API大綱圖:


DOM相關包概覽

  • org.w3c.dom:為XML文件定義了DOM程式設計介面,由W3C制定
  • javax.xml.parsers:定義了DocumentBuilderFactory 類和 DocumentBuilder 類,DocumentBuilder 返回一個代表W3C Document 的介面。包中也定義了用於報告錯誤的ParserConfigurationException異常類

XSLT相關API


XSLT API概覽圖:


XSLT相關包說明:

  • javax.xml.transform:定義了TransformerFactory 類和 Transformer 類, 你可以用於得到有轉換能力的物件,得到轉換物件後用輸入(source)和輸出(result)引數呼叫transform() 方法
  • javax.xml.transform.dom:包含由DOM建立輸入和輸出物件的類
  • javax.xml.transform.sax:包含由SAX解析器建立輸入物件和由SAX事件處理器建立輸出物件的類
  • javax.xml.transform.stream:包含由I/O流建立輸入和輸出物件的類

StAX API簡介

StAX更多具體介紹可以看這裡:http://blog.csdn.net/zhangyihui1986/article/details/8528649

相關包說明:

  • javax.xml.stream:定義了用於迭代XML文件元素的XMLStreamReader 介面和規定如何寫XML的XMLStreamWriter 介面
  • javax.xml.transform.stax:提供與StAX轉換相關的 APIs.

JAXP各相關API特徵對比

XML Parser API Feature Summary

特徵

StAX

SAX

DOM

TrAX

API Type

Pull, streaming

Push, streaming

In memory tree

XSLT Rule

Ease of Use

High

Medium

High

Medium

XPath Capability

No

No

Yes

Yes

CPU and Memory Efficiency

Good

Good

Varies

Varies

Forward Only

Yes

Yes

No

No

Read XML

Yes

Yes

Yes

Yes

Write XML

Yes

No

Yes

Yes

Create, Read, Update, Delete

No

No

Yes

No

JAXP更改語法分析器


上面提到JAXP是抽象而非具體的API,它可以使用符合規範的其它XML語法分析器;其實切換語法分析器很簡單,就是切換獲取語法分析的工廠類而已,因為所有 SAXParser 和 DocumentBuilder 例項都來自這些類工廠。

更改語法分析器通過設定Java系統屬性來實現。可以通過設定 Java 系統屬性 javax.xml.parsers.SAXParserFactory 來更改要使用的 SAXParserFactory 介面實現;如果沒有定義該屬性,則返回預設實現。相同原理適用於 DocumentBuilderFactory 實現,在這種情況下,將查詢 javax.xml.parsers.DocumentBuilderFactory 系統屬性。

你可以通過閱讀相關的JDK原始碼來證明(以SAXParserFactory為例):

首先看一下SAXParserFactory類的newInstance方法:

package javax.xml.parsers;

// imports

public abstract class SAXParserFactory {
    /** The default property name according to the JAXP spec */
    private static final String DEFAULT_PROPERTY_NAME = "javax.xml.parsers.SAXParserFactory";

    protected SAXParserFactory () {}

    public static SAXParserFactory newInstance() {
        try {
            return (SAXParserFactory) FactoryFinder.find(
                /* The default property name according to the JAXP spec */
                "javax.xml.parsers.SAXParserFactory",
                /* The fallback implementation class name */
                "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
        } catch (FactoryFinder.ConfigurationError e) {
            throw new FactoryConfigurationError(e.getException(), e.getMessage());
        }
    }
}

跟著去FactoryFinder類(該修飾符不是public,因此在JDK的API文件中找不到)看一下欺find()方法:
static Object find(String factoryId, String fallbackClassName) throws ConfigurationError {              
        // 首先根據factoryId去系統屬性中查詢實現類, 存在則將其例項化並返回
        try {
            String systemProp = ss.getSystemProperty(factoryId);
            if (systemProp != null) {                
                return newInstance(systemProp, null, true);
            }
        }  catch (SecurityException se) {
            if (debug) se.printStackTrace();
        }

        // 讀取$java.home/lib/jaxp.properties檔案, 如果存在鍵為factoryId的屬性, 例項化並返回
        try {
            String factoryClassName = null;
            if (firstTime) {
                synchronized (cacheProps) {
                    if (firstTime) {
                        String configFile = ss.getSystemProperty("java.home") + File.separator +
                            "lib" + File.separator + "jaxp.properties";
                        File f = new File(configFile);
                        firstTime = false;
                        if (ss.doesFileExist(f)) {
                            cacheProps.load(ss.getFileInputStream(f));
                        }
                    }
                }
            }
            factoryClassName = cacheProps.getProperty(factoryId);            

            if (factoryClassName != null) {
                return newInstance(factoryClassName, null, true);
            }
        } catch (Exception ex) {
            if (debug) ex.printStackTrace();
        }

        // Try Jar Service Provider Mechanism
        Object provider = findJarServiceProvider(factoryId);
        if (provider != null) {
            return provider;
        }
        if (fallbackClassName == null) {
            throw new ConfigurationError("Provider for " + factoryId + " cannot be found", null);
        }
		// 最後例項化預設的Factory並返回
        return newInstance(fallbackClassName, null, true);
    }

這個方法雖然很長,但結構很清晰。結合SAXParserFactory類的newInstance方法可以看出,系統首先拿著javax.xml.parsers.SAXParserFactory作為鍵去系統屬性中查詢實現類, 存在則將其例項化並返回;如果系統屬性中沒有指定,則讀取$java.home/lib/jaxp.properties配置檔案, 如果存在鍵為factoryId的屬性值, 由例項化並返回;如果最後一直沒有找到合適的Factory,則例項化預設的Factory,在這裡是呼叫FacotyrFinder類中的find方法傳入的第二個引數com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl,由此也可以看出,預設採用的是Apache的Xerces語法分析器。

同樣的分析可以應用於DocumentBuilderFactory類,這裡不予贅述。

其實StAX解析方式也是類似的,只不過它分了XMLInputFactory和XMLOutputFactory兩個類,對應的系統屬性值分別是javax.xml.stream.XMLInputFactory和javax.xml.stream.XMLOutputFactory,而預設實現分別是com.sun.xml.internal.stream.XMLInputFactoryImpl和com.sun.xml.internal.stream.XMLOutputFactoryImpl。這裡也可以看出是Sun內部的語法分析器。