盘点 SpringIOC : Resource 及 Document 体系

2,637 阅读12分钟

总文档 :文章目录
Github : github.com/black-ant

一 . 前言

第二篇 IOC 的文章还是挑一个软柿子捏 , 这一篇说一说 Resource 和 Document 的加载方式 , 也是比较简单的东西.

扫描部分的体系包括 Resource , Document ,Annotation 几部分 , 这里只说前面2种

二 . Resource 体系篇

Resource 是一切的基础 , 所有的外部对象都可以看成一个 Resource , 为此 Spring 提供了很多 Resource 的实现类 :

Resource.png

常见的 Resource 主要是用于以下功能:

// 当用不同的 ResourceLoader 加载资源的时候 , 会根据资源类型的不同 , 选择生成不同的 Resource

C- ByteArrayResource : 给定字节数组的资源实现 , 用于从任何给定的字节数组加载内容,而不必求助于单一使用的InputStreamResource
C- ClassPathResource : 类路径资源的资源实现 , 使用给定的ClassLoader或给定的Class来加载资源
C- ContextResource : 用于从封闭的“上下文”加载资源的扩展接口
C- DescriptiveResource : 保存资源描述但不指向实际可读资源的简单资源实现
C- EncodedResource : 将资源描述符与用于从资源中读取的特定编码或字符集组合在一起
C- FileSystemResource : 资源实现处理一个文件系统目标
C- FileUrlResource : 它假定文件解析,达到实现WritableResource接口的程度
C- HttpResource : 将资源写入HTTP响应的扩展接口
C- ImportResource : 指示一个或多个包含要导入的bean定义的资源
C- InputStreamResource : 给定InputStream的资源实现 , 只在没有其他特定资源实现适用的情况下使用
C- ServletContextResource : ServletContext资源的资源实现,解释web应用程序根目录中的相对路径
C- VfsResource : 基于JBoss VFS的资源实现
C- WritableResource : 支持向资源写入的资源的扩展接口。提供一个输出流访问器

Resource 体系功能 :

// 统一资源的核心类是 : Resource , 为 Spring 框架所有资源的抽象和访问接口 
I- Resource
    E- InputStreamSource

C- AbstractResource
    I- Resource
    
> 以上是统一资源管理中核心的三个类 ,他们的继承关系如上
    : Spring 中所有的资源都可以用 Resource 表示
    : AbstractResource 继承自 Resource ,并且对其做了实现
	
> Resource 中有很多常见的功能
    exists / isReadable / isOpen / isFile / getURL / getFile / readableChannel 
    contentLength / lastModified / createRelative / getFileName / getDescription
	
> AbstractResource 有以下通用的实现 
    - FileSystemResource : 对 java.io.File 类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道
        - 支持文件和 URL 的形式,实现 WritableResource 接口,从 Spring Framework 5.0 开始,FileSystemResource 使用 NIO2 API进行读/写交互
    - ByteArrayResource : 对字节数组提供的数据的封装。
    - URIResource : 对 java.net.URL类型资源的封装。内部委派 URL 进行具体的资源操作。
    - ClassPathResource : class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。
    - InputStreamResource : 将给定的 InputStream 作为一种资源的 Resource 的实现类。
    
C- AbstractResource
    - exists() : 判断文件是否存在
    

Resource 接口的实现


C- AbstractResource 
    TODO
    

Resource 会通过 ResourceLoader 进行加载 , 重要的 ResourceLoader 包含如下结构 :

ResourceLoader.png

需要注意的是 : ApplicationContext 基本上都是 ResourceLoader 的实现类 , 所以他们通常带有ResourceLoader 的功能

ResourceLoader加载体系


> Spring 通过 ResourceLoader 来进行 资源的加载

C11- ResourceLoader : 资源的加载
    M- getResource() : 根据所提供资源的路径 location 返回 Resource 实例
        - 支持 URL位置资源 / ClassPath位置资源 / 相对路径资源
    M- getClassLoader() : 返回 ClassLoader 实例
    MC- ResourceLoader(ClassLoader)
         - Thread.currentThread().getContextClassLoader()
         - ClassUtils.getDefaultClassLoader()
         - setClassLoader()


    : interface ResourcePatternResolver extends ResourceLoader
    
C18- DefaultResourceLoader
    MC- DefaultResourceLoader
        - ClassUtils.getDefaultClassLoader();
    MC- DefaultResourceLoader(@Nullable ClassLoader classLoader)
    M- addProtocolResolver(ProtocolResolver) : 自定义的 Resolver 加入 Spring 体系
    M- getResource(String location)
        - 首先,通过 ProtocolResolver 来加载资源 , 成功返回 Resource 
        - 其次,以 / 开头,调用 #getResourceByPath() 方法, 返回 ClassPathContextResource 类型的资源
        - 再次,以 classpath: 开头,返回 ClassPathResource 类型的资源
            - 通过#getClassLoader() 获取当前的 ClassLoader
        - 然后,根据是否为文件 URL ,是则返回 FileUrlResource 类型的资源,否则返回 UrlResource 类型的资源
            - 最后,返回 ClassPathContextResource 类型的资源
            
// resourceLoader.getResource("D:/Users/chenming673/Documents/spark.txt");
    
C- ResourcePatternResolver : ResourceLoader 的默认实现
    M- setClassLoader / getClassLoader

> FileSystemResourceLoader
    内部类 : FileSystemContextResource extends FileSystemResource

C- ProtocolResolver : 用户自定义协议资源解决策略
    ?- 作为 DefaultResourceLoader 的 SPI:它允许用户自定义资源加载协议,而不需要继承 ResourceLoader 的子类
    ?- 现 ProtocolResolver 接口也可以实现自定义的 ResourceLoader
    M-  resolve(String , ResourceLoader )

一个 Resource 加载流程中涉及的主要类

// XML 流程 Resource 加载 , 我们还是从 BeanDefinitionReader 开始看起
C160- AbstractBeanDefinitionReader
	M- loadBeanDefinitions(location, null) 
	M- loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)
    	- getResourceLoader 获取一个 ResourceLoader
    	- ResourceLoader 通过 location 获取 Resource[] -> 
    	- 讲获取的  Resource 加入 actualResources , 用于后方处理
    
C16- ResourcePatternResolver 
    
    
C51- GenericApplicationContext
    M51_033- getResources(String locationPattern)
    
    
C17- PathMatchingResourcePatternResolver
    M17_02- getResources(String locationPattern)
    	- classpath*: 开头 , 则分别调用 findPathMatchingResources (-> ) / findAllClassPathResources
    	- getResourceLoader() 调用获取 Resource -> M18_05
    M17_03- findPathMatchingResources
    	- 获取路径 ,递归获取包路径
        - 通过包 URLResource 调用 doFindPathMatchingFileResources 获取类
        
    M17_04- findAllClassPathResources
    	- 调用 doFindAllClassPathResources 获取 classResource
        
    M17_05- doFindAllClassPathResources
        - 获取一个 ClassLoader , 通过 ClassLoader 获取 resource url
        - 通过 convertClassLoaderURL 对 URL 列表转换为 UrlResource
            ?- 这里其实还是包路径 -> PS:M17_05_01 
            
        
        
// PS:M17_05_01 
file:/D:/java/workspace/git/case/case%20Origin%20Source/case%20SpringBootIOC/target/classes/com/gang/study/source/springboot/demo/
        
    
C18- DefaultResourceLoader
	M18_05- getResource(String location)
         - 如果存在 ProtocolResolvers 集合, 则循环集合 , 试图用 ProtocolResolver 处理返回
		- 如果是 / 开头 , 则生成一个 ClassPathContextResource
		- 如果是 classpath 打头 ,new 创建出一个 ClassPathResource , 并且为其配置一个 ClassLoader
            	?- 所以 , 这里 bean.xml 是被映射为 ClassPathResource
		- 如果是 URL 类型 , 构建为一个 FileUrlResource
            
            
// M17_03 伪代码
// locationPattern -- classpath*:com/gang/study/source/springboot/demo/**/*.class
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
    // classpath*:com/gang/study/source/springboot/demo/
    String rootDirPath = determineRootDir(locationPattern);
    // **/*.class
    String subPattern = locationPattern.substring(rootDirPath.length());
    
    // 扫描路径 , 将路径下资源转换为Resource数组
    Resource[] rootDirResources = getResources(rootDirPath);
    Set<Resource> result = new LinkedHashSet<>(16);
    for (Resource rootDirResource : rootDirResources) {
        rootDirResource = resolveRootDirResource(rootDirResource);
        URL rootDirUrl = rootDirResource.getURL();
        if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
            URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
            if (resolvedUrl != null) {
                rootDirUrl = resolvedUrl;
            }
            rootDirResource = new UrlResource(rootDirUrl);
        }
        if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            // VFS 的加载方式
            result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
        }else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
            // JAR 包路径的加载
            result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
        }else {
            // 加载类
            result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
        }
    }
    return result.toArray(new Resource[0]);
}
            

加载 Resource 的几个场景 , 通常可以由 location 看出来

类型一 : classpath*:com/gang/study/source/springboot/demo/**/*.class

//这种路径 , 其源头为  ComponentScanAnnotationParser 开始 , 更早的源头是 Configuration 的相关扫描逻辑

C153- ComponentScanAnnotationParser
    M153_01- parse : 由该方法扫描处理 ComponentScan  -> M155_03
        
C155- ClassPathBeanDefinitionScanner 
    M155_03- doScan(String... basePackages) -> M201_03
        
C201- ClassPathScanningCandidateComponentProvider
    M201_03- scanCandidateComponents(String basePackage)
        - 构建一个地址 -> PS:201_03_01
        - 调用 ResourcePatternResolver(AnnotationConfigServletWebServerApplicationContext) 获取 Resouce
            - 最终调用 M17_02
        
C17- PathMatchingResourcePatternResolver
    M17_02- getResources(String locationPattern) 
        - Class 前缀 , 最终调用 M17_03
    M17_03- findPathMatchingResources
    M17_04- findAllClassPathResources
        
        
        
// M201_03 伪代码
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
       //...................
    	String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
        	resolveBasePackage(basePackage) + '/' + this.resourcePattern;
       // PS:201_03_01
       // com.gang.study.source.springboot.demo 转变为
       // classpath*:com/gang/study/source/springboot/demo/**/*.class
    	Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        //...................
        
            
}


前置资源的加载 :


sequenceDiagram
M153_01->>M155_03: ComponentScan 扫描类路径
M155_03->>M201_03: doScan 扫描路径 
M201_03->>M17_02: 调用 PathMatchingResourcePatternResolver <br>获取 resource

Resource 资源的加载 :

  • M17_02 : getResources
  • M17_03 : findPathMatchingResources
  • M17_04 : findAllClassPathResources
  • M17_05 : doFindAllClassPathResources

sequenceDiagram
M17_02->>M17_03: 发现是 待匹配的classpath resource <br>调用 findPathMatchingResources
M17_03->>M17_03: 递归 Resource 
M17_03-->>M17_02: 回调 getResources
M17_02-->>M17_04: 发现为 classPath <br> 调用findAllClassPathResources 
M17_04->>M17_03: 返回具体的classPath 即 URLReource (实际)
M17_03->>M17_05: 调用 classloader 扫描具体的类对象
M17_05->>M17_03: 返回对象 , 放入集合

核心逻辑就是 :

  • 如果路径是待匹配的 : findPathMatchingResources
  • 其中通过匹配 , 调用 findAllClassPathResources 获取真实路径
  • 通过真实路径 , 调用 doFindAllClassPathResources 获取具体的类 Resource

类型二 : classpath*:messages.properties 资源加载

前置资源的加载 :


// 这种情况的加载主要来源于 Configuration , 例如 MessageSourceAutoConfiguration
// 此处对 message 资源进行加载 , 调用 ResourceBundleCondition 进行资源加载


C202- ResourceBundleCondition
    M202_01- getMatchOutcomeForBasename
        
    M202_02- getResources
        - 通过一个 classLoader 构建了一个 PathMatchingResourcePatternResolver 
        - 调用 getResources 返回相关的 resource 资源
            ?- 这里因为需要指定的资源 , 所以方式是定下了的
        
// M202_02 伪代码
private Resource[] getResources(ClassLoader classLoader, String name) {
    String target = name.replace('.', '/');
    return new PathMatchingResourcePatternResolver(classLoader).getResources("classpath*:" + target + ".properties");
			
}

// 同样的 , 调用了 PathMatchingResourcePatternResolver
// 此处无需匹配 , 直接 调用 findAllClassPathResources
C17- PathMatchingResourcePatternResolver
    M17_02- getResources(String locationPattern) 
        - 无需匹配 , 直接 调用 findAllClassPathResources ->  M17_04
    M17_04- findAllClassPathResources
   


sequenceDiagram
M202_02->>M17_02: 获取 classpath*:messages.properties
M17_02->>M17_04: 无需匹配 , 直接 调用 findAllClassPathResources
M17_04-->>M17_02: 返回资源对于 URLResource
M17_02-->>M202_02: Configuration 使用资源

类型三 : xml 类型资源

以 classpath:spring-common.xml为例



// 起源 : 
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    ?- 即 ImportedResources 注解导致

// 流程 : 
C160- AbstractBeanDefinitionReader
    M- loadBeanDefinitions
        - ((ResourcePatternResolver) resourceLoader).getResources(location)
            ?- 注意 , 这里的 Resources 仍然是 pring-common.xml 这个对象

// 后续继续调用 XmlBeanDefinitionReader , 这里就不详细说了

PS: 至于其他的不具有代表性 , 就不详细说了

三 . Document 体系篇

当统一资源加载完成后 , 其中的 xml 类型会被处理为 Document 对象

Document 扫描是指对文档体系的扫描 , 主要用于xml 方式配置 Bean 时

PS : 说一句 , 早期的时候我也觉得说 XML 配置很陈旧 , 是过时的方法 , 但是现在反而有种不一样的感觉 , 相比Configuration 方式 ,它的条理更清晰 , 用起来其实也不复杂

Document 体系功能

我们先看下 Doument 的常见方法功能 :

作用 : 用于处理 Document 文档类型对象 , 包括 .xml , .dto , .schemas 等

起源 : XmlBeanDefinitionReader#doLoadDocument(InputSource inputSource, Resource resource) 方法

  • 该方法做了2件事
    • 调用 #getValidationModeForResource(Resource resource) 方法,获取指定资源(xml)的验证模式
    • 调用 DocumentLoader#loadDocument 获取 XML Document 实例
C25- DocumentLoader : 获取 Document 的策略,由接口 org.springframework.beans.factory.xml.DocumentLoader 定义
    P- inputSource 方法参数,加载 Document 的 Resource 资源。
    P- entityResolver 方法参数,解析文件的解析器。
    P- errorHandler 方法参数,处理加载 Document 对象的过程的错误。
    P- validationMode 方法参数,验证模式。
    P- namespaceAware 方法参数,命名空间支持。如果要提供对 XML 名称空间的支持,则需要值为 true 。

C26- DefaultDocumentLoader : DocumentLoader 的默认实现类 
    M26_01- loadDocument
        - 首先,调用 #createDocumentBuilderFactory(...) 方法,创建 javax.xml.parsers.DocumentBuilderFactory 对象
            - DocumentBuilderFactory.newInstance(); -- 创建 DocumentBuilderFactory
            - factory.setNamespaceAware(namespaceAware);  --  设置命名空间支持
        - 调用 #createDocumentBuilder 方法,创建 javax.xml.parsers.DocumentBuilder 对象
            - 创建 DocumentBuilder 对象
            - 设置 EntityResolver 属性
            - 设置 ErrorHandler 属性
        - 调用 DocumentBuilder#parse(InputSource) 方法,解析 InputSource ,返回 Document 对象
 
// XmlBeanDefinitionReader :     
    M- getEntityResolver() : 返回指定的解析器,如果没有指定,则构造一个未指定的默认解析器
        -1 ResourceLoader resourceLoader = getResourceLoader();
        IF-2 resourceLoader != null 
            - this.entityResolver = new ResourceEntityResolver(resourceLoader);
        ELSE-2 
            - this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());

Document 扫描跟踪 :


// 起点 : Document 的起点是 , 这里会通过一个 Resource 加载对于的 xml , 将 XML 转换为 Document
C- ConfigurationClassBeanDefinitionReader
    M- loadBeanDefinitionsFromImportedResources 

// 中间的逻辑比较简单 , 我们直接从 XmlBeanDefinitionReader 开始看
    
C21- XmlBeanDefinitionReader
    M21_01- doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        - 调用 doLoadDocument  , 将  Resource 转换为 Document -> M23_01
        - 调用 registerBeanDefinitions(doc, resource) 注册Bean
    M21_02- doLoadDocument
        - documentLoader.loadDocument : loader 加载
    

C- DocumentLoader

C23- DefaultDocumentLoader
    I- DocumentLoader
    M23_01- loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
        - createDocumentBuilderFactory 创建一个 DocumentBuilderFactory -> M23_02
        - 通过 DocumentBuilderFactory 创建一个 DocumentBuilder -> M23_03
        - DocumentBuilder parse 方法解读 Document
    M23_02- createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
        - 通过 newInstance 生成 DocumentBuilderFactory 
        - 为 DocumentBuilderFactory 设置 NamespaceAware , Validating , 以及 Attribute
            ?- 注意 , 这里会判断 validationMode 类型来设置
    M23_03- createDocumentBuilder(DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
        - 通过传入的工厂生成 DocumentBuilder
        - 为 DocumentBuilder 设置 EntityResolver 和 ErrorHandler
    
    
// M23_01 补充
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}


// M23_02 补充
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware){
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(namespaceAware);
    if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
        factory.setValidating(true);
        if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
            factory.setNamespaceAware(true);
            factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
            //.... 省略catch
        }
    }
    return factory;
}


// N23_03
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
            @Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
            throws ParserConfigurationException {

        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        if (entityResolver != null) {
            docBuilder.setEntityResolver(entityResolver);
        }
        if (errorHandler != null) {
            docBuilder.setErrorHandler(errorHandler);
        }
        return docBuilder;
}  
    

DocumentBuilder

DocumentBuilder 是一个抽象类 , 其主要 实现是 DocumentBuilderImpl


C24- DocumentBuilder
    F- DOMParser domParser;
    F- EntityResolver fInitEntityResolver
    F- ErrorHandler fInitErrorHandler
    MC- DocumentBuilderImpl
    M24_01- parse
        - domParser.parse(is) 
        - domParser getDocument 获得 Document
        
// 这里涉及到 com.sun.org.apache.xerces.internal.parsers.DOMParser 对象
C- DOMParser
    ?- 用于对 DOM 类型解析处理

完整 Document 处理流程图

// Step 1 : ConfigurationClassBeanDefinitionReader # loadBeanDefinitions
    ?- 用于加载 class类上面的 classpath load
// Step 2 : AbstractBeanDefinitionReader # loadBeanDefinitions
    ?- 开始加载 xml 文件
// Step 3 : XmlBeanDefinitionReader # loadBeanDefinitions
    ?- 处理加载的 resource 对象 , xml resource 的加载可以看上文
    
// Step 4 : 主要处理流程开始
C21- XmlBeanDefinitionReader
    M21_01- doLoadBeanDefinitions
        - doLoadDocument(inputSource, resource) -> M21_02
    M21_02- doLoadDocument
    
    
C26- DefaultDocumentLoader : DocumentLoader 的默认实现类 
    M26_01- loadDocument
    
C24- DocumentBuilder
      M24_01- parse


sequenceDiagram
M21_01->>M21_01: 加载 bean.xml  , 获得对象的 resource 文件
M21_01->>M21_02: doLoadDocument 处理 resource 对象
M21_02-->>M26_01: DocumentBuilderFactory -> DocumentBuilder<br> 加载document
M21_02-->>M24_01: 解析 Document 
M24_01-->>M21_02: 返回 Document 对象
M21_02-->>M21_01: 处理 document 对象 -> registerBeanDefinitions

扩展 EntityResolver 对象

EntityResolver 对象用于对 Document 进行解析 , 现在常见的有这几种 :

参考文档 @ cmsblogs.com/?p=2695

  • ResourceEntityResolver:继承自 EntityResolver ,通过 ResourceLoader 来解析实体的引用。
  • DelegatingEntityResolver:EntityResolver 的实现,分别代理了 dtd 的 BeansDtdResolver 和 xml schemas 的 PluggableSchemaResolver。
  • BeansDtdResolver : spring bean dtd 解析器。EntityResolver 的实现,用来从 classpath 或者 jar 文件加载 dtd。
  • PluggableSchemaResolver:使用一系列 Map 文件将 schema url 解析到本地 classpath 资源
protected EntityResolver getEntityResolver() {
    if (this.entityResolver == null) {
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader != null) {
            this.entityResolver = new ResourceEntityResolver(resourceLoader);
        }else {
            this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
        }
    }
    return this.entityResolver;
}

补充 Document 功能

Document 主要就是为了将 XML 解释为可读对象

Document.png

补充 DTD 和 XSD

参考文档 @ cmsblogs.com/?p=2688 , 建议看看原版 , 这里摘取些关键环节

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 的发展,但是它本身存在着一些缺陷:

  • 它没有使用 XML 格式,而是自己定义了一套格式,相对解析器的重用性较差;
  • DTD 的构建和访问没有标准的编程接口,因而解析器很难简单的解析 DTD 文档。
  • DTD 对元素的类型限制较少;同时其他的约束力也叫弱。
  • DTD 扩展能力较差。
  • 基于正则表达式的 DTD 文档的描述能力有限。

XSD 具有如下优势 针对 DTD 的缺陷,W3C 在 2001 年推出 XSD。XSD(XML Schemas Definition)即 XML Schema 语言。XML Schema 本身就是一个 XML文档,使用的是 XML 语法,因此可以很方便的解析 XSD 文档。

  • XML Schema基于XML,没有专门的语法
  • XML Schema可以象其他XML文件一样解析和处理
  • XML Schema比DTD提供了更丰富的数据类型.
  • XML Schema提供可扩充的数据模型。
  • XML Schema支持综合命名空间
  • XML Schema支持属性组。

不同的验证模式使用不同的解析器解析 :

ValidationMode 的验证位置


C21- XmlBeanDefinitionReader
    M21_02- doLoadDocument -> PS:M21_02_01
    
// PS:M21_02_01 此处获取了 ValidationMode
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());


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


// 常量
/**
* Indicates that the validation mode should be auto-guessed, since we cannot find
* a clear indication (probably choked on some special characters, or the like).
*/
public static final int VALIDATION_AUTO = 1;

/**
* Indicates that DTD validation should be used (we found a "DOCTYPE" declaration).
*/
public static final int VALIDATION_DTD = 2;

/**
* Indicates that XSD validation should be used (found no "DOCTYPE" declaration).
*/
public static final int VALIDATION_XSD = 3;


总结

Resource 和 Document 的扫描收集是一切的基础 ,当2者采集完全后 , 就可以开始相关类的加载工作了