天天看點

Spring源碼解析 第二章、容器的基本實作

2.6  擷取XML的驗證方式

加載Bean的過程主要有三步,1、擷取對XML的驗證模式  2、加載XML檔案,并且得到相應的doucument  3、根據傳回的doucument來注冊Bean的資訊。接下來具體探讨一下Spring源碼中怎麼去完成的這三步。

2.6.1  DTD與XSD的差別

DTD:類型定義(Documnet Type Definition) 

DTD 是一套關于标記符的文法規則。它是XML1.0版規格得一部分,是XML檔案的驗證機制,屬于XML檔案組成的一部分。 

DTD 是一種保證XML文檔格式正确的有效方法,可以通過比較XML文檔和DTD檔案來看文檔是否符合規範,元素和标簽使用是否正确。 

一個DTD文檔包含:元素的定義規則,元素間關系的定義規則,元素可使用的屬性,可使用的實體或符号規則。 

但是DTD 是使用非 XML 文法編寫的 

DTD 不可擴充,不支援命名空間,隻提供非常有限的資料類型 

XSD:XML結構定義 ( XML Schemas Definition ) 

XML Schema語言也就是XSD。XML Schema描述了XML文檔的結構。 

可以用一個指定的XML Schema來驗證某個XML文檔,以檢查該XML文檔是否符合其要求。文檔設計者可以通過XML Schema指定一個XML文檔所允許的結構和内容,并可據此檢查一個XML文檔是否是有效的。XML Schema本身是一個XML文檔,它符合XML文法結構。可以用通用的XML解析器解析它。 

一個XML Schema會定義:文檔中出現的元素、文檔中出現的屬性、子元素、子元素的數量、子元素的順序、元素是否為空、元素和屬性的資料類型、元素或屬性的預設和固定值。 

XSD是DTD替代者的原因,一是據将來的條件可擴充,二是比DTD豐富和有用,三是用XML書寫,四是支援資料類型,五是支援命名空間。 

XML Schema的優點: 

1) XML Schema基于XML,沒有專門的文法 

2) XML可以象其他XML檔案一樣解析和處理 

3) XML Schema支援一系列的資料類型(int、float、Boolean、date等) 

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

5) XML Schema支援綜合命名空間 

6) XML Schema支援屬性組。

2.6.2  驗證模式的讀取 

之前分析過Spring通過getValidationModeForResource(resource)這個方法進入到擷取XML驗證模式,下面看一下這個方法的源碼

protected int getValidationModeForResource(Resource resource) {
		int validationModeToUse = getValidationMode();
        //自定義的驗證方式
		if (validationModeToUse != VALIDATION_AUTO) {
			return validationModeToUse;
		}
        // 如果未指定驗證方式,那麼将會自動檢測
		int detectedMode = detectValidationMode(resource);
		if (detectedMode != VALIDATION_AUTO) {
			return detectedMode;
		}
		return VALIDATION_XSD;
	}
           

方法的實作其實是很簡單的,無非是如果設定了驗證模式則使用驗證模式(可以通過調用XmlBeanDefinitionReader中的setValidationModeName(String validationModeName)來進行自定義驗證),否則執行detectValidationMode(resource)使用自動檢測的方式。在detectValidationMode方法中,又将自動檢測的驗證模式委托給了ValidationModeDetector類,調用了detectValidationMode的方法。

protected int detectValidationMode(Resource resource) {1
		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);
		}
        //委托了validationModeDetector
		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);
		}
	}
           
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;
				}
				if (hasDoctype(content)) {
					isDtdValidated = true;
					break;
				}
                //當讀到<這個得時候,則進行開始驗證了,因為dtd,xsd都是以<開始的
				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();
		}
	}
           

其實自動檢測的業務邏輯很簡單,就是判斷是否包含DOCTYPE,如果包含就是DTD驗證方式,否則就是XSD

繼續閱讀