1. 程式人生 > >XML的驗證模式

XML的驗證模式

前言

  上一篇文章講述了bean的載入的最關鍵的三個步驟,這三個步驟很重要,是支撐著Spring容器實現的基礎,所以這三個步驟是值得仔細認真的去學習的,其中第一個步驟就是獲取XML的驗證模式,這篇文章就是講述這個步驟。

DTD和XSD

DTD:

  DTD(Document Type Definition)即文件型別定義。是一種XML約束模式語言,是XML檔案的驗證機制,屬於XML檔案組成的一部分。DTD是一種保證XML文件格式正確的有效方法,可以通過比較XML文件和DTD檔案來看文件是否符合規範,元素和標籤使用是否正確。

  一個DTD文件包含:元素的定義規則、元素間關係的定義規則、元素可使用的屬性、可使用的實體或符號規則。

  要使用DTD驗證模式的時候需要在XML檔案頭部宣告,以下是Spring中使用DTD宣告方式的程式碼:

<?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文件的描述能力有限。

XSD:

  針對DTD的缺陷,W3C在2001年推出XSD。XSD(XML Schemas Definition)就是XML Schema語言。XML Schema描述了XML文件的結構。可以用一個指定的XML Schema來驗證某個XML文件,以檢查該XML文件是否符合其要求。文件設計者可以通過XML Schema指定一個XML文件所允許的結構和內容,並可據此檢查一個XML文件是否是有效的。XML Schema本身是一個XML文件,它符合XML語法結構。可以使用通用的XML解析器解析它。

  在使用XML Schema文件對XML例項文件進行檢驗,除了要宣告名稱空間外(xmlns=http://www.Springframework.org/schema/beans),還必須指定該名稱空間所對應的XML Schema文件的儲存位置。通過schemaLocation屬性來指定名稱空間所對應的XML Schema文件的儲存位置,它包含兩個部分,一部分是名稱空間的URI,另一部分就是該名稱空間所標識的XML Schema檔案位置或URL地址(xsi:schemaLocation="http://www.Springframework.org/schema/beans http://www.Springframework.org/schema/beans/Spring-beans.xsd")。

  XSD具有以下優勢:

  (1)XML Schema基於XML,符合XML語法結構。

  (2)XML Schema可以像其他XML檔案一樣解析和處理。

  (3)XML Schema比DTD提供更豐富的資料型別。

  (4)XML Schema提供可擴充的資料模型。

  (5)XML Schema支援綜合名稱空間。

  (6)XML Schema支援屬性組。

驗證模式的獲取

  在瞭解了DTD和XSD 的區別後,我們再來分析Spring中對於驗證模式的讀取就更容易理解了。通過我們上一篇文章的分析,我們鎖定了getValidationModeForResource方法來獲取對應資源的驗證模式,下面來看一下這個方法的原始碼:

 1   protected int getValidationModeForResource(Resource resource) {
 2         int validationModeToUse = getValidationMode();
 3         if (validationModeToUse != VALIDATION_AUTO) {
 4             return validationModeToUse;
 5         }
 6         int detectedMode = detectValidationMode(resource);
 7         if (detectedMode != VALIDATION_AUTO) {
 8             return detectedMode;
 9         }
10         // Hmm, we didn't get a clear indication... Let's assume XSD,
11         // since apparently no DTD declaration has been found up until
12         // detection stopped (before finding the document's root tag).
13         return VALIDATION_XSD;
14     }

  可以看出,方法的實現其實還是很簡單的,在程式碼第3行的意思是:如果手動指定了驗證模式則使用指定的驗證模式。程式碼第6行:如果未指定則使用自動檢測。所以,該方法的邏輯就是:如果設定了驗證模式則使用設定的驗證模式(可以通過呼叫 XmlBeanDefinitionReader中的setValidationMode方法進行設定),否則使用自動檢測的方式。

  而自動檢測驗證模式的功能是在函式detectValidationMode方法中實現的,在detectValidationMode函式中又將自動檢測驗證模式的工作委託給了專門處理類XmlValidationModeDetector,呼叫了XMLValidationModeDetector的validationModeDetector方法,具體方法如下:

XmlBeanDefinitionReader.detectValidationMode(Resource resource):

 1 protected int detectValidationMode(Resource resource) {
 2         if (resource.isOpen()) {
 3             throw new BeanDefinitionStoreException(
 4                     "Passed-in Resource [" + resource + "] contains an open stream: " +
 5                     "cannot determine validation mode automatically. Either pass in a Resource " +
 6                     "that is able to create fresh streams, or explicitly specify the validationMode " +
 7                     "on your XmlBeanDefinitionReader instance.");
 8         }
 9 
10         InputStream inputStream;
11         try {
12             inputStream = resource.getInputStream();
13         }
14         catch (IOException ex) {
15             throw new BeanDefinitionStoreException(
16                     "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
17                     "Did you attempt to load directly from a SAX InputSource without specifying the " +
18                     "validationMode on your XmlBeanDefinitionReader instance?", ex);
19         }
20 
21         try {
22             return this.validationModeDetector.detectValidationMode(inputStream);
23         }
24         catch (IOException ex) {
25             throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
26                     resource + "]: an error occurred whilst reading from the InputStream.", ex);
27         }
28     }

XmlValidationModeDetector.detectValidationMode(InputStream inputStream):

 1 public int detectValidationMode(InputStream inputStream) throws IOException {
 2         // Peek into the file to look for DOCTYPE.
 3         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
 4         try {
 5             boolean isDtdValidated = false;
 6             String content;
 7             while ((content = reader.readLine()) != null) {
 8                 content = consumeCommentTokens(content);
 9                 if (this.inComment || !StringUtils.hasText(content)) {
10                     continue;
11                 }
12                 if (hasDoctype(content)) {
13                     isDtdValidated = true;
14                     break;
15                 }
16                 if (hasOpeningTag(content)) {
17                     // End of meaningful data...
18                     break;
19                 }
20             }
21             return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
22         }
23         catch (CharConversionException ex) {
24             // Choked on some character encoding...
25             // Leave the decision up to the caller.
26             return VALIDATION_AUTO;
27         }
28         finally {
29             reader.close();
30         }
31     }

  第一段程式碼沒有什麼需要解釋的,就是呼叫了XMLValidationModeDetector類的detectValidationMode(inputStream)方法。

  第二段程式碼第9行:如果讀取的行是空或者是註釋則略過。程式碼第16行:讀取到“<”開始符號時直接退出,因為驗證模式一定在開始符號之前。

  總的來說,Spring用來檢測驗證模式的辦法就是判斷是否包含DOCTYPE,如果包含就是DTD,否則就是XSD。

至此,已經將載入Bean的第一個步驟,獲取驗證模式分析完畢,下篇文章將講述Bean載入的第二個步驟:獲取Document例項。

參考:《Spring原始碼深度解析》 郝佳 編著: