通俗理解spring源碼(三)—— 獲取xml的驗證模式

来源:https://www.cnblogs.com/xiaohang123/archive/2020/04/15/12709192.html
-Advertisement-
Play Games

通俗理解spring源碼(三)—— 獲取xml的驗證模式 上一篇講到了xmlBeanDefinitionReader.doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法。 protected int doLoadBe ...


通俗理解spring源碼(三)—— 獲取xml的驗證模式

上一篇講到了xmlBeanDefinitionReader.doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            //從資源文件轉換為document對象
            Document doc = doLoadDocument(inputSource, resource);
            //解析document,並註冊beanDefiniton到工廠中
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }

 在該方法中,首先就是將資源文件裝換為document對象

    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }

   通過getValidationModeForResource(resource)獲取xml文件的驗證模式。

xml文件有兩種校驗模式,DTD和XSD,這裡簡單介紹一下:

1、DTD校驗模式

DTD(Document Type Definition)即文檔類型定義,是一種xml約束模式語言,是xml文件的驗證機制,屬於xml文件的一部分。DTD是一種保證xml文檔格式正確的有效方法,可以通過比較xml文檔和DTD文件來看文檔是否符合規範,元素和標簽使用是或否正確。一個DTD文檔包含:元素的定義規則,元素間關係的定義規則,元素可使用的屬性,可使用的實體或符號規則。

這個DTD文件,可以直接寫在xml內部,如:

<?xml version="1.0"?>
<!DOCTYPE note [
  <!ELEMENT note (to,from,heading,body)>
  <!ELEMENT to      (#PCDATA)>
  <!ELEMENT from    (#PCDATA)>
  <!ELEMENT heading (#PCDATA)>
  <!ELEMENT body    (#PCDATA)>
]>
<note>
  <to>George</to>
  <from>John</from>
  <heading>Reminder</heading>
  <body>Don't forget the meeting!</body>
</note>

也可以外部引用,比如將DTD內容寫在與xml文件同目錄的note.dtd中,如:

<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note> 

   還可以引用網路上的DTD文件,如在我們最熟悉的mybatis配置文件中:

<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">

  引用外部DTD文件,一定會有<!DOCTYPE >聲明!

  關於DTD文檔的詳細語法,可以參考https://www.w3school.com.cn/dtd/index.asp

2、XSD驗證模式

  XML Schema語言就是XSD(XML Schemas Definition)。XML Schema描述了xml文檔的結構,可以用一個指定的XML Schema來驗證某個XML文檔,以檢查該xml文檔是否符合要求。文檔設計者可以通過XML Schema指定xml文檔所允許的結構和內容,並可據此檢查xml文檔是否是有效的。XML Schema本身是xml文檔,它符合xml語法結構。可以用通用的xml‘解析器解析它。

  XSD比DTD更加強大,可針對未來的需求進行擴展,基於 XML 編寫,支持數據類型,支持命名空間等。

  一個xml文件中可以引入多個命名空間,每個命名空間都要與一個首碼綁定,或者沒有首碼,作為預設命名空間,並且每個命名空間都要指定其對應的xml Schema文件位置或URL位置,如在spring配置文件中:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd">

</beans>

  其中,

xmlns="http://www.springframework.org/schema/beans
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

  表示引入beans作為預設命名空間,相對應的xsd文件在http://www.springframework.org/schema/beans/spring-beans-4.3.xsd中,要使用該命名空間的標簽,不用加首碼。

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd

   表示引入context命名空間,並與context首碼相綁定,相對應的xsd文件在http://www.springframework.org/schema/context/spring-context-4.3.xsd中,即要使用該命名空間的標簽,需要加context首碼,比如說我們最熟悉的  <context:component-scan base-package=""></context:component-scan>。

  關於XSD文檔的詳細語法,可以參考https://www.w3school.com.cn/schema/index.asp

 3、驗證模式的讀取

  瞭解了DTD和XSD的區別後再去分析spring中對於驗證模式的獲取就容易多了。

  接著來看getValidationModeForResource(resource)。

    protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }
        // Hmm, we didn't get a clear indication... Let's assume XSD,
        // since apparently no DTD declaration has been found up until
        // detection stopped (before finding the document's root tag).
        return VALIDATION_XSD;
    }

   這裡邏輯很簡單,作者的註釋也很有意思,就是說我們無法清楚的知道準確的驗證模式,如果在找到文檔的根標簽之前還沒有找到明顯的DTD聲明,則推測為XSD驗證模式。

  繼續看一下detectValidationMode(resource)方法:

    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);
        }
    }

   又是委派模式,由validationModeDetector進行處理,進入validationModeDetector.detectValidationMode(inputStream)中:

    public int detectValidationMode(InputStream inputStream) throws IOException {
        // Peek into the file to look for DOCTYPE.
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            boolean isDtdValidated = false;
            String content;
            //一行行讀取文件內容
            while ((content = reader.readLine()) != null) {
                //去掉文件的註釋內容
                content = consumeCommentTokens(content);
                if (this.inComment || !StringUtils.hasText(content)) {
                    continue;
                }
                //判斷該行是否包含DOCTYPE這個字元串
                if (hasDoctype(content)) {
                    isDtdValidated = true;
                    break;
                }
                //判斷該行是否包含開始標簽符號,即"<"
                if (hasOpeningTag(content)) {
                    // End of meaningful data...
                    break;
                }
            }
            return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
        }
        catch (CharConversionException ex) {
            // Choked on some character encoding...
            // Leave the decision up to the caller.
            return VALIDATION_AUTO;
        }
        finally {
            reader.close();
        }
    }

    private boolean hasDoctype(String content) {
        return content.contains(DOCTYPE);
    }
    private boolean hasOpeningTag(String content) {
        if (this.inComment) {
            return false;
        }
        int openTagIndex = content.indexOf('<');
        return (openTagIndex > -1 && (content.length() > openTagIndex + 1) &&
                Character.isLetter(content.charAt(openTagIndex + 1)));
    }

  一行行讀取文件內容,去掉文件的註釋內容,首先判斷該行是否包含DOCTYPE這個字元串,如果有則判定為VALIDATION_DTD,如果沒有,再判斷該行是否包含開始標簽符號,如果有,則判定VALIDATION_XSD,如果沒有,則讀取下一行。

 

  獲取xml驗證模式的邏輯並不複雜,主要是要知道DTD和XSD的區別。

  走的太遠,不要忘記為什麼出發!獲取校驗模式的目的是要對xml文件進行校驗,然後解析成document。

    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }

   下一章將講解documentLoader.loadDocument,獲取Document。

 

參考:https://www.w3school.com.cn/

   https://www.cnblogs.com/osttwz/p/6892999.html

   spring源碼深度解析


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 北航OO(2020)第二單元博客作業 [TOC] 設計策略分析(多線程視角) 本單元的三次作業中,我採用了相似的策略:採用輸入線程與電梯線程通過線程安全的調度器進行交互的方式。這種方式基本屬於生產者 消費者模式。在調度器的設計方面,我主要採用synchronized關鍵字結合wait和notify方 ...
  • 前言 這是一個基於中小型企業或團隊的架構設計。 不考慮大廠。有充分的理由相信,大廠有絕對的實力來搭建一個相當複雜的環境。 中小型企業或團隊是個什麼樣子? 開發團隊人員配置不全,部分人員身兼開發過程上下游的數個職責; 沒有專職的維護人員,或者維護人員實力不足以完全掌控生產和開發環境。 這種情況下,過於 ...
  • 無處不在的線程,多線程,阻塞隊列,併發 編程世界無新鮮事,看你翻牆翻得厲不厲害 場景:現在的軟體開發迭代速度(一周一更新,甚至一天一發佈)真是太快了,今天進行軟體更新的時候,看到了有趣的現象,這不就是線程池,ThreadPoolExecutor,阻塞隊列,任務(下載和安裝)最好的案例嘛!經常看到很多 ...
  • redis為什麼那麼快?結論有三點,大家都知道,這裡主要是分析。 首先第一點 redis是記憶體訪問的,所以快 當然這個大家都知道,所以不是重點 io密集型和cpu密集型 一般我們把任務分為io密集型和cpu密集型 io密集型 IO密集型指的是系統的CPU性能相對硬碟、記憶體要好很多,此時,系統運作,大 ...
  • 一、製作Nine-Patch圖片 1.含義:一種被特殊處理的png圖片,能夠指定哪些區域可以被拉伸,哪些區域不可以被拉伸。 2.首先先製作一個佈局 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xml ...
  • 實例級別的鎖 實例代碼 @Slf4j public class AddCompareDemo { private int a, b; public void add() { for (int i = 0; i < 10000; i++) { a++; b++; } } public void com ...
  • ThreadLocal ThreadLocal 適用於變數線上程間隔離,而在方法或類間共用的場景。 代碼 1 @RestController 2 public class ThreadLocalController { 3 private static final ThreadLocal<Strin ...
  • 指針是一個代表著某個記憶體地址的值, 這個記憶體地址往往是在記憶體中存儲的另一個變數的值的起始位置. Go語言對指針的支持介於Java語言和 C/C++ 語言之間, 它既沒有像Java那樣取消了代碼對指針的直接操作的能力, 也避免了 C/C++ 中由於對指針的濫用而造成的安全和可靠性問題. 指針地址和變數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...