1. 程式人生 > >【死磕 Spring】----- IOC 之 獲取驗證模型

【死磕 Spring】----- IOC 之 獲取驗證模型

close 步驟 call buffere n) 規範 frame create ring

原文出自:http://cmsblogs.com

在上篇博客【死磕Spring】----- IOC 之 加載 Bean 中提到,在核心邏輯方法 doLoadBeanDefinitions()中主要是做三件事情。

  1. 調用 getValidationModeForResource() 獲取 xml 文件的驗證模式
  2. 調用 loadDocument() 根據 xml 文件獲取相應的 Document 實例。
  3. 調用 registerBeanDefinitions() 註冊 Bean 實例。

這篇博客主要分析獲取 xml 文件的驗證模式。

XML 文件的驗證模式保證了 XML 文件的正確性

DTD 與 XSD 的區別

DTD(Document Type Definition),即文檔類型定義,為 XML 文件的驗證機制,屬於 XML 文件中組成的一部分。DTD 是一種保證 XML 文檔格式正確的有效驗證方式,它定義了相關 XML 文檔的元素、屬性、排列方式、元素的內容類型以及元素的層次結構。其實 DTD 就相當於 XML 中的 “詞匯”和“語法”,我們可以通過比較 XML 文件和 DTD 文件 來看文檔是否符合規範,元素和標簽使用是否正確。

要在 Spring 中使用 DTD,需要在 Spring XML 文件頭部聲明:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">

DTD 在一定的階段推動了 XML 的發展,但是它本身存在著一些缺陷:

  1. 它沒有使用 XML 格式,而是自己定義了一套格式,相對解析器的重用性較差;而且 DTD 的構建和訪問沒有標準的編程接口,因而解析器很難簡單的解析 DTD 文檔。
  2. DTD 對元素的類型限制較少;同時其他的約束力也叫弱。
  3. DTD 擴展能力較差。
  4. 基於正則表達式的 DTD 文檔的描述能力有限。

針對 DTD 的缺陷,W3C 在 2001 年推出 XSD。XSD(XML Schemas Definition)即 XML Schema 語言。XML Schema 本身就是一個 XML文檔,使用的是 XML 語法,因此可以很方便的解析 XSD 文檔。相對於 DTD,XSD 具有如下優勢:

  • XML Schema基於XML,沒有專門的語法
  • XML Schema可以象其他XML文件一樣解析和處理
  • XML Schema比DTD提供了更豐富的數據類型.
  • XML Schema提供可擴充的數據模型。
  • XML Schema支持綜合命名空間
  • XML Schema支持屬性組。

getValidationModeForResource() 分析

    protected int getValidationModeForResource(Resource resource) {
        // 獲取指定的驗證模式
        int validationModeToUse = getValidationMode();
        // 如果手動指定,則直接返回
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        // 通過程序檢測
        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }
        
        // 出現異常,返回 XSD
        return VALIDATION_XSD;
    }

如果指定了 XML 文件的的驗證模式(調用XmlBeanDefinitionReader.setValidating(boolean validating))則直接返回指定的驗證模式,否則調用 detectValidationMode() 獲取相應的驗證模式,如下:

    protected int detectValidationMode(Resource resource) {
        if (resource.isOpen()) {
            throw new BeanDefinitionStoreException(
                    "Passed-in Resource [" + resource + "] contains an open stream: " +
                    "cannot determine validation mode automatically. Either pass in a Resource " +
                    "that is able to create fresh streams, or explicitly specify the validationMode " +
                    "on your XmlBeanDefinitionReader instance.");
        }

        InputStream inputStream;
        try {
            inputStream = resource.getInputStream();
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
                    "Did you attempt to load directly from a SAX InputSource without specifying the " +
                    "validationMode on your XmlBeanDefinitionReader instance?", ex);
        }

        try {
          // 核心方法
            return this.validationModeDetector.detectValidationMode(inputStream);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
                    resource + "]: an error occurred whilst reading from the InputStream.", ex);
        }
    }

前面一大堆的代碼,核心在於 this.validationModeDetector.detectValidationMode(inputStream),validationModeDetector 定義為 XmlValidationModeDetector,所以驗證模式的獲取委托給 XmlValidationModeDetectordetectValidationMode() 方法。

    public int detectValidationMode(InputStream inputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            boolean isDtdValidated = false;
            String content;
            // 一行一行讀取 xml 文件的內容
            while ((content = reader.readLine()) != null) {
                content = consumeCommentTokens(content);
                if (this.inComment || !StringUtils.hasText(content)) {
                    continue;
                }
                // 包含 DOCTYPE 為 DTD 模式
                if (hasDoctype(content)) {
                    isDtdValidated = true;
                    break;
                }
                // 讀取 < 開始符號,驗證模式一定會在 < 符號之前
                if (hasOpeningTag(content)) {
                    // End of meaningful data...
                    break;
                }
            }
        // 為 true 返回 DTD,否則返回 XSD
            return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
        }
        catch (CharConversionException ex) {
            // 出現異常,為 XSD
            return VALIDATION_AUTO;
        }
        finally {
            reader.close();
        }
    }

從代碼中看,主要是通過讀取 XML 文件的內容,判斷內容中是否包含有 DOCTYPE ,如果是 則為 DTD,否則為 XSD,當然只會讀取到 第一個 "<" 處,因為 驗證模式一定會在第一個 “<” 之前。如果當中出現了 CharConversionException 異常,則為 XSD模式。

好了,XML 文件的驗證模式分析完畢,下篇分析 doLoadBeanDefinitions() 的第二個步驟:獲取 Document 實例。


技術分享圖片

【死磕 Spring】----- IOC 之 獲取驗證模型