作为一个Spring初学者,你是否常常被XML文件的配置头信息,搞得焦头烂额,不知所措?

作为一个Spring资深老鸟,你是否也还不清楚xsi:schemaLocation的真正作用?

这一篇文章,告诉你所有的细节!

以下是,Spring XML的文件头配置信息,相信很多人对此已非常熟悉。


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
      http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans.xsd">
      
</beans>


01 Schema-based Configuration

什么是XML Schema、XSD?

XML Schema是一种用来描述,XML文档结构的语言规范。

目前各家采用的都是,符合W3C标准定义的XML Schema规范。

Spring从2.5版本开始,全面拥抱XML Schema。

另外,还有大家熟知的Maven:


<project xmlns="http://maven.apache.org/POM/4.0.0" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
          http://maven.apache.org/POM/4.0.0 
          http://maven.apache.org/xsd/maven-4.0.0.xsd">
          
</project>


XSD(XML Schema Definition)它是定义XML Schema规范的具体描述语言。

Spring以及大家熟知的Dubbo框架,都自行实现了符合XML Schema规范的XSD文件。

下一篇我将详细介绍,Dubbo是如何借助Spring Schema的扩展能力,来加载dubbo.xsd文件的。

什么是命名空间xmlns?

xmlns(XML NameSpace)它的标准定义是:xmlns:prefix=“Namespace-name-URI”,其中prefix是一个别名,你可以使用任何你喜欢的名称来表示。

prefix不是必须的,根(Root)命名空间默认可以不用加prefix。如上一段代码:


xmlns=“http://www.springframework.org/schema/beans”


prefix的作用:使用prefix可以区分不同xmlns中定义的相同属性值,不至于产生混乱。

举个例子,想必大家都有这样的经历:

班上出现了两个都叫张三的同学,平时为了区分,大家把其中一个同学叫大张三,另外一个同学叫小张三,这里的大和小,就像是两位张三同学拥有的prefix一样,有了这个prefix,相信班上所有与张三同学相关的事情,都清晰多了。

为什么xmlns的值看起来像一个URL?

Namespace-name-URI仅表示一个字符串常量,它与是否要从网络上加载资源是没有任何关系的。

NameSpace讲究全局唯一,而URL(统一资源定位符)天生具备唯一性。

什么是xmlns:xsi ?

从前面我们知道,Spring XML遵循W3C XML Schema规范。

所以,引入此段文字就相当于声明了,当前XML文件符合W3C 规范。

换句话说,符合W3C规范的XML文件,都必须引入这段文字:


xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”。


那为什么是形如xmlns:xsi,而不是xmlns:xsixsi或其它形式呢?

让我们来看看,W3C官方是如何解释的:


schema version_XML


官方的意思是,写成xmlns:xsi只是大家的习惯而已。事实上,写成xmlns:xsixsi也是没有问题的。

为了达到认知上的统一,大家习惯性的写成xmlns:xsi而已。

难道引入这段话的目的,仅仅是为了声明XML文件符合W3C规范吗?

其实不然。

正确理解schemaLocation

引入xmlns:xsi的另一个核心目的是:使用xsi命名空间下的schemaLocation。

在具体介绍schemaLocation之前,我们先看看,W3C官方是如何解释的:


schema version_schema version_02


从官方描述中,可以得到以下几点信息:

第一:schemaLocation是“http://www.w3.org/2001/XMLSchema-instance”里的属性值。

第二:schemaLocation里定义的值,需要“成对”出现。

很多同学一直不明白为什么要成对出现。

W3C给出了官方解释:每个成对值的第一个值,仅仅表示之前已经声明过的某个xmlns的值;第二个值表示的是,当前这个xmlns所对应的xsd文件的具体路径。

这里敲一下黑板:第二个值是一个URL路径,它代表了获取xsd文件的访问路径;也就是说,它可以从一个网络地址上获取到。

然而,无论是Spring还是MyBatis采用的方式,都是代理到本地路径下,从本地获取xsd文件。

schemaLocation更重要的意义在于:它提供了一套xsd文件的加载规范。

我们可以在schemaLocation中,扩展自定义的xsd文件。

Spring就是利用这种机制,来加载spring-beans.xsd文件的。

从另一个角度讲,只有schemaLocation中的“成对”值,才会被真正加载使用。

理解了上述内容,我们就可以很好的管理Spring XML文件头配置信息了。

02 XSD文件的合法性验证

XSD文件定义好之后,接下来一步就是验证文件的合法性。

即,确认该文件是否符合W3C XML Schema规范。

Spring使用Java自带的JAXP(JavaAPI for XML Processing)来解析验证XML。

除JAXP之外,还可以使用SAX或DOM来做相同的工作。

一起来看看,Spring的处理源码:


protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
   throws ParserConfigurationException {

  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  factory.setNamespaceAware(namespaceAware);

  if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
   factory.setValidating(true);
   if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
    // Enforce namespace aware for XSD...
    factory.setNamespaceAware(true);
    try {
     factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
    }
    catch (IllegalArgumentException ex) {
     ParserConfigurationException pcex = new ParserConfigurationException(
       "Unable to validate using XSD: Your JAXP provider [" + factory +
       "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
       "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
     pcex.initCause(ex);
     throw pcex;
    }
   }
  }
  return factory;
}


第一步:获取JAXP文档解析器


DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();


第二步:启用对XML文件的检验


factory.setValidating(true);


默认情况下,JAXP验证的不是XSD类型的文件,而是DTD类型的文件

第三步:强制启用XSD验证

1、设置namespaceAware=true; 默认情况下:false


factory.setNamespaceAware(true);


启用schemaLocation对Namespace的感知

2、设置下面两个固定值,


public class DefaultDocumentLoader implements DocumentLoader { 
// 请搜索,DefaultDocumentLoader类查看
private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";


factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE,XSD_SCHEMA_LANGUAGE)


通过设置上述属性值,可以将XML文件的验证方式从DTD切换到XSD。

以下是setAttribute()方法的doc描述:


schema version_xml_03


从doc描述中可以看出,正是通过这一步的设置,使得JAXP解析器可以处理schemaLocation中定义的xsd文件集合。

至此,我们分析了XSD文件的验证逻辑,但未涉及XSD文件的加载过程,此部分内容将在下章节展开讲。

届时,我们将了解Dubbo利用Spring Schema扩展机制,加载dubbo.xsd文件的技术原理。