zoukankan      html  css  js  c++  java
  • Spring5源码分析(009)——IoC篇之加载BeanDefinition:获取 Document 实例

    注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总


      上一篇《Spring5源码分析(007)——IoC篇之加载BeanDefinition总览》 中提到,加载 bean 的核心方法 doLoadBeanDefinitions(InputSource inputSource, Resource resource) 中,分为3个步骤来进行:

    • 1、通过调用 getValidationModeForResource(Resource resource) 来获取指定 XML 资源的验证模式,也即是 xml 开头常见到的各种 DTD 和 XSD 了。(参考博客:Spring5源码分析(008)——IoC篇之加载BeanDefinition:获取XML的验证模式
    • 2、通过调用 DocumentLoader.loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) 来获取实际的 Document 实例。
    • 3、调用 registerBeanDefinitions(Document doc, Resource resource),根据 Document 解析和注册 BeanDefinition。

      本文主要介绍第2个步骤,也就是获取 Document 实例,目录结构如下:

      XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法并没有亲自处理 Document 实例的加载获取,而是委托给专门的处理接口 DocumentLoader ,通过调用 DocumentLoader.loadDocument 来获取 Document 实例,这里实际使用的是其默认实现类 DefaultDocumentLoader 。 

    1、DocumentLoader

      org.springframework.beans.factory.xml.DocumentLoader :定义从资源文件加载到转换为 Document 的功能。其内部接口声明如下:

    /**
     * Strategy interface for loading an XML {@link Document}.
     * <p>加载 XML 的策略接口
     */
    public interface DocumentLoader {
    
       /**
        * Load a {@link Document document} from the supplied {@link InputSource source}.
        * @param inputSource the source of the document that is to be loaded 将要加载的 document 的 Resource 输入资源
        * @param entityResolver the resolver that is to be used to resolve any entities 解析器
        * @param errorHandler used to report any errors during document loading 处理加载文档过程中出现的错误
        * @param validationMode the type of validation XML验证模式
        * {@link org.springframework.util.xml.XmlValidationModeDetector#VALIDATION_DTD DTD}
        * or {@link org.springframework.util.xml.XmlValidationModeDetector#VALIDATION_XSD XSD})
        * @param namespaceAware {@code true} if support for XML namespaces is to be provided 是否支持 XML 命名空间,true 表示支持 XML 命名空间
        * @return the loaded {@link Document document}
        * @throws Exception if an error occurs
        */
       Document loadDocument(
             InputSource inputSource, EntityResolver entityResolver,
             ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
             throws Exception;
    
    }

       摘抄一下入参说明:

    • inputSource: the source of the document that is to be loaded ,将要加载的 document 的 Resource 输入资源
    • entityResolver: the resolver that is to be used to resolve any entities ,解析器
    • errorHandler: used to report any errors during document loading ,处理加载文档过程中出现的错误
    • validationMode: the type of validation ,XML验证模式
    • namespaceAware: true if support for XML namespaces is to be provided ,是否支持 XML 命名空间,true 表示支持 XML 命名空间

    2、DefaultDocumentLoader

      实际上进行处理的是 DocumentLoader 的默认实现类 org.springframework.beans.factory.xml.DefaultDocumentLoader ,具体如下:

    /**
     * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
     * XML parser.
     * <p>在提供的 InputSource 上通过标准的 JAXP 配置的 XML 解析器来加载 Document 实例
     */
    @Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
        // 1、创建 DocumentBuilderFactory 实例
        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isTraceEnabled()) {
            logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        // 2、创建 DocumentBuilder 实例
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        // 3、解析 XML InputSource 然后返回 Document 实例对象
        return builder.parse(inputSource);
    }

       这里其实就是通过 SAX 解析 XML 文档。首先是创建 DocumentBuilderFactory ,再通过 DocumentBuilderFactory 创建 DocumentBuilder ,进而解析 inputSource 来返回 Document 实例对象。

    • 首先,1、创建 DocumentBuilderFactory 实例:这里通过调用 createDocumentBuilderFactory(int validationMode, boolean namespaceAware) 方法来创建 javax.xml.parsers.DocumentBuilderFactory 实例对象:
    /**
     * Create the {@link DocumentBuilderFactory} instance.
     * <p>创建 DocumentBuilderFactory 实例
     * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}
     * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})
     * @param namespaceAware whether the returned factory is to provide support for XML namespaces
     * @return the JAXP DocumentBuilderFactory
     * @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory
     */
    protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
            throws ParserConfigurationException {
        // 创建 DocumentBuilderFactory 实例
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(namespaceAware);    // 设置命名空间支持
        // 有验证模式
        if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
            factory.setValidating(true);    // 开启校验
            // XSD 验证模式
            if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
                // Enforce namespace aware for XSD...
                factory.setNamespaceAware(true);    // XSD 验证模式支持命名空间
                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;
    }
    • 然后,2、创建 DocumentBuilder 实例:通过调用 createDocumentBuilder(DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler) 方法,创建 javax.xml.parsers.DocumentBuilder 实例对象:
    /**
     * Create a JAXP DocumentBuilder that this bean definition reader
     * will use for parsing XML documents. Can be overridden in subclasses,
     * adding further initialization of the builder.
     * <p>创建这个 bean definition reader 用于解析 XML 文档的 JAXP DocumentBuilder。
     * 子类可以重写,以便为 builder 添加更多的初始化配置。
     * @param factory the JAXP DocumentBuilderFactory that the DocumentBuilder
     * should be created with
     * @param entityResolver the SAX EntityResolver to use
     * @param errorHandler the SAX ErrorHandler to use
     * @return the JAXP DocumentBuilder
     * @throws ParserConfigurationException if thrown by JAXP methods
     */
    protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
            @Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
            throws ParserConfigurationException {
    
        // 创建 DocumentBuilder 对象
        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        // 设置 EntityResolver 属性
        if (entityResolver != null) {
            docBuilder.setEntityResolver(entityResolver);
        }
        // 设置 ErrorHandler 属性
        if (errorHandler != null) {
            docBuilder.setErrorHandler(errorHandler);
        }
        return docBuilder;
    }
    •  最后,3、解析 XML InputSource 然后返回 Document 实例对象:通过调用 DocumentBuilder.parse(org.xml.sax.InputSource is) 对 InputSource 进行解析,返回对应的 Document 实例对象。

      这部分代码和通常的通过 SAX 解析 XML 文档的方式大致一致。 Spring 在这里的处理并没有什么特殊的地方,同样首先创建 DocumentBuilderFactory ,再通过 DocumentBuilderFactory 创建 DocumentBuilder ,进而解析 inputSource 来返回 Document 对象。

      这里需要注意的是 DocumentBuilder 的 EntityResolver 属性,接下来会进行讲解。

    3、EntityResolver

      在 DocumentLoader.loadDocument(...) 方法中涉及一个参数 EntityResolver ,该参数是通过 XmlBeanDefinitionReader.getEntityResolver() 方法来获取的,代码如下:

    /**
     * Return the EntityResolver to use, building a default resolver
     * if none specified.
     * <p>返回使用的 EntityResolver ,如果没有指定的话返回默认的 resolver
     */
    protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            // Determine default EntityResolver to use.
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            }
            else {
                this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return this.entityResolver;
    }
    •  如果 ResourceLoader 不为 null ,则根据指定的 ResourceLoader 创建一个 ResourceEntityResolver 对象。(这里的 resourceLoader 基本上就是之前提到的 PathMatchingResourcePatternResolver ,而 ResourceEntityResolver 是 DelegatingEntityResolver 的子类)
    • 如果 ResourceLoader 为 null ,则创建一个 DelegatingEntityResolver 对象。该 Resolver 委托给默认的 BeansDtdResolver 和 PluggableSchemaResolver 。

      后面会对这几个子类进行说明。这里先回到 org.xml.sax.EntityResolver (JDK 本身的类),它有什么作用呢?我们先看下 EntityResolver 的 javadoc 说明:


      如果 SAX 应用程序需要实现对外部实体的定制处理,它就必须实现这个接口并使用 setEntityResolver 方法向 SAX 驱动程序注册一个实例。

      然后, XML reader 将允许应用程序在包含任何外部实体之前拦截它们(包括外部 DTD 子集和外部参数实体,如果有的话)。

      许多 SAX 应用程序将不需要实现这个接口,但是对于从数据库或其他专用输入源构建 XML 文档的应用程序,或者使用 url 以外的 URI 类型的应用程序,它将特别有用。

      下面这个解析器将为系统标识符为“http://www.myhost.com/today”的实体提供一个特殊的字符流:

    import org.xml.sax.EntityResolver;
    import org.xml.sax.InputSource;
    
    public class MyResolver implements EntityResolver {
        public InputSource resolveEntity (String publicId, String systemId)
        {
            if (systemId.equals("http://www.myhost.com/today")) {
                 // return a special input source
                 MyReader reader = new MyReader();
                return new InputSource(reader);
            } else {
                // use the default behaviour
                return null;
            }
        }
    }

       应用程序还可以使用这个接口将系统标识符重定向到本地 uri,或者在目录中查找替换的标识符(可能使用公共标识符)。


       再来看看《Spring源码深度解析(第2版)》P38 的一段说明:

      对于解析一个 XML, SAX 首先读取该 XML 文档上的声明,根据声明去寻找相应的 DTD 定义,以便对文档进行一个验证。 默认的寻找规则,即通过网络(实现上就是声明的 DTD 的 URI 地址)来下载相应的 DTD 声明,并进行认证。下载的过程是一个漫长的过程,而且当网络中断或不可用时,这里会报错,就是因为相应的 DTD 声明没有被找到的原因。

      EntityResolver 的作用是项目本身就可以提供一个如何寻找 DTD 声明的方法,即由程序来实现寻找 DTD 声明的过程,比如我们将 DTD 文件放到项目中某处 ,在实现时直接将此文档读取并返回给 SAX 即可。 这样就避免了通过网络来寻找相应的声明。

      这下子就清晰点了,EntityResolver 的作用:通过实现 EntityResolver 来自定义如何寻找【DTD/XSD 验证文件】的逻辑。EntityResolver 接口代码如下: 

    public interface EntityResolver {
    
        public abstract InputSource resolveEntity (String publicId, String systemId)
            throws SAXException, IOException;
    
    }

       唯一的方法 InputSource resolveEntity (String publicId, String systemId) 有2个入参 publicId 和 systemId ,返回的是 org.xml.sax.InputSource 对象(null 则表示解析器需要打开一个常规的系统标识符 URI 连接)。2个参数声明如下:

    • publicId :被引用的外部实体的公共标识符,如果没有提供,则是 null
    • systemId :被引用的外部实体的系统标识符。

      这两个参数于具体的验证模式关系如下:

    • XSD 验证模式
      • 配置如下:
    <?xml version="l.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>
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd" >
    <beans>
    </beans>

    3.1、EntityResolver 的4个子类

      前面的 getEntityResolver() 方法一共提到了 EntityResolver 的4个子类:ResourceEntityResolver / DelegatingEntityResolver / BeansDtdResolver / PluggableSchemaResolver ,接下来一一进行介绍。

    3.1.1、BeansDtdResolver

      org.springframework.beans.factory.xml.BeansDtdResolver : EntityResolver 实现类,Spring beans DTD 的解析器,从 Spring 的 classpath 或者 JAR 文件中加载 DTD。具体实现也是很直接:检查是否 .dtd 后缀,然后再判定是否包含 spring-beans ,是的话,之后便是构造 ClassPathResource 对象(类路径下的 spring-beans.dtd ,即 /org/springframework/beans/factory/xml/spring-beans.dtd ),通过此 ClassPathResource 的输入流再构造 InputSource ,并设置内部的 publicId、systemId 属性,然后返回。如果都不是,或者构造失败,则直接返回 null 。代码如下:

    /**
     * <p>EntityResolver 实现类,Spring beans DTD 的解析器,从 Spring 的 classpath 或者 JAR 文件中加载 DTD。
     * <p>无论是否在 DTD 名称中指定了包含 "spring-beans" 的 URL,
     * 还是使用 "https://www.springframework.org/dtd/spring-beans-2.0.dtd",
     * 都会从 classpath 资源 "/org/springframework/beans/factory/xml/spring-beans.dtd" 下
     * 抓取 spring-beans.dtd" 文件 ,
     *
     */
    public class BeansDtdResolver implements EntityResolver {
        // dtd 扩展名
        private static final String DTD_EXTENSION = ".dtd";
        // Spring beans DTD 的文件名
        private static final String DTD_NAME = "spring-beans";
    
        @Override
        @Nullable
        public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws IOException {
            if (logger.isTraceEnabled()) {
                logger.trace("Trying to resolve XML entity with public ID [" + publicId +
                        "] and system ID [" + systemId + "]");
            }
            // 以 .dtd 结尾
            if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
                // 获取最后一个 / 的索引位置
                int lastPathSeparator = systemId.lastIndexOf('/');
                // 获取 spring-beans 的位置
                int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
                // 存在 spring-beans
                if (dtdNameStart != -1) {
                    String dtdFile = DTD_NAME + DTD_EXTENSION;
                    if (logger.isTraceEnabled()) {
                        logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
                    }
                    try {
                        // 创建 ClassPathResource 对象,相对于当前类对象路径的资源,其实就是 /org/springframework/beans/factory/xml/spring-beans.dtd
                        Resource resource = new ClassPathResource(dtdFile, getClass());
                        // 创建 InputSource 对象,并设置 publicId 和 systemId
                        InputSource source = new InputSource(resource.getInputStream());
                        source.setPublicId(publicId);
                        source.setSystemId(systemId);
                        if (logger.isTraceEnabled()) {
                            logger.trace("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
                        }
                        return source;
                    }
                    catch (FileNotFoundException ex) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
                        }
                    }
                }
            }
    
            // Fall back to the parser's default behavior.
            return null;
        }
    
    }

      

    3.1.2、PluggableSchemaResolver

      org.springframework.beans.factory.xml.PluggableSchemaResolver :EntityResolver 实现类,使用一组映射文件将模式 url 解析为本地类路径资源的 EntityResolver 实现。默认情况下,读取 classpath 下所有的 META-INF/spring.schemas ,转化成一个 schema URL 与 local schema path 的 map 。PluggableSchemaResolver 的解析过程如下:

    /**
     * The location of the file that defines schema mappings.
     * Can be present in multiple JAR files.
     * <p>定义 schema 映射的文件路径,可以出现在多个JAR文件中。
     */
    public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
    
    @Nullable
    private final ClassLoader classLoader;
    // schema 映射文件路径
    private final String schemaMappingsLocation;
    
    // schema URL 到 本地 schema 路径 的映射集合
    /** Stores the mapping of schema URL -> local schema path. */
    @Nullable
    private volatile Map<String, String> schemaMappings;
    
    @Override
    @Nullable
    public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws IOException {
        if (logger.isTraceEnabled()) {
            logger.trace("Trying to resolve XML entity with public id [" + publicId +
                    "] and system id [" + systemId + "]");
        }
    
        if (systemId != null) {
            // 获取 systemId 对应的 Resource 所在路径
            String resourceLocation = getSchemaMappings().get(systemId);
            if (resourceLocation == null && systemId.startsWith("https:")) {
                // Retrieve canonical http schema mapping even for https declaration
                // 若申明的 https 映射找不到,找检索对应的 http 模式映射
                resourceLocation = getSchemaMappings().get("http:" + systemId.substring(6));
            }
            if (resourceLocation != null) {
                // 创建 ClassPathResource 实例对象
                Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
                try {
                    // 创建 InputSource 对象,并设置 publicId 和 systemId
                    InputSource source = new InputSource(resource.getInputStream());
                    source.setPublicId(publicId);
                    source.setSystemId(systemId);
                    if (logger.isTraceEnabled()) {
                        logger.trace("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
                    }
                    return source;
                }
                catch (FileNotFoundException ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not find XML schema [" + systemId + "]: " + resource, ex);
                    }
                }
            }
        }
    
        // Fall back to the parser's default behavior.
        return null;
    }

       上面提到的 schema URL 与 local schema path 的 map ,其实是通过调用 getSchemaMappings() 方法来实现的:

    /**
     * Load the specified schema mappings lazily.
     * <p>懒加载指定的 schema mappings ,默认从 "META-INF/spring.schemas" 进行加载
     */
    private Map<String, String> getSchemaMappings() {
        Map<String, String> schemaMappings = this.schemaMappings;
        // 双重检查锁,实现 schemaMappings 单例
        if (schemaMappings == null) {
            synchronized (this) {
                schemaMappings = this.schemaMappings;
                if (schemaMappings == null) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
                    }
                    try {
                        // 加载 schemaMappingsLocation(默认是"META-INF/spring.schemas")文件中的属性
                        Properties mappings =
                                PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
                        if (logger.isTraceEnabled()) {
                            logger.trace("Loaded schema mappings: " + mappings);
                        }
                        schemaMappings = new ConcurrentHashMap<>(mappings.size());
                        // 将 Properties 转成 schemaMappings
                        CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
                        this.schemaMappings = schemaMappings;
                    }
                    catch (IOException ex) {
                        throw new IllegalStateException(
                                "Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
                    }
                }
            }
        }
        return schemaMappings;
    }

       spring-beans 模块中默认的 META-INF/spring.schemas 文件如下,可以看到都映射到了类路径下的 xsd 了。

    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    http://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool-4.3.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    http://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util-4.3.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    http://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans-4.3.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
    https://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool-4.3.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
    https://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util-4.3.xsd=org/springframework/beans/factory/xml/spring-util.xsd
    https://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util.xsd

      

    3.1.3、DelegatingEntityResolver

      org.springframework.beans.factory.xml.DelegatingEntityResolver :这个从命名就可以大概知道是个委托类,内部其实是直接委托给 BeansDtdResolverPluggableSchemaResolver 来进行解析,实现任意类型的解析( dtd 和 xsd )。

    public class DelegatingEntityResolver implements EntityResolver {
    
        // dtd 文件后缀
        /** Suffix for DTD files. */
        public static final String DTD_SUFFIX = ".dtd";
    
        // xsd 文件后缀
        /** Suffix for schema definition files. */
        public static final String XSD_SUFFIX = ".xsd";
    
        // dtd 解析器
        private final EntityResolver dtdResolver;
    
        // xsd 解析器
        private final EntityResolver schemaResolver;
    
        /**
         * Create a new DelegatingEntityResolver that delegates to
         * a default {@link BeansDtdResolver} and a default {@link PluggableSchemaResolver}.
         * <p>创建一个新的 DelegatingEntityResolver 实例对象,委托给默认的 BeansDtdResolver 和默认的 PluggableSchemaResolver 。
         *
         * <p>Configures the {@link PluggableSchemaResolver} with the supplied
         * {@link ClassLoader}.
         * <p>使用提供的类加载器配置 PluggableSchemaResolver 。
         *
         * @param classLoader the ClassLoader to use for loading
         * (can be {@code null}) to use the default ClassLoader)
         */
        public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
            this.dtdResolver = new BeansDtdResolver();
            this.schemaResolver = new PluggableSchemaResolver(classLoader);
        }
    
        @Override
        @Nullable
        public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId)
                throws SAXException, IOException {
    
            if (systemId != null) {
                // .dtd 后缀的解析,即 DTD 模式
                if (systemId.endsWith(DTD_SUFFIX)) {
                    return this.dtdResolver.resolveEntity(publicId, systemId);
                }
                // .xsd 后缀的解析,即 XSD 模式
                else if (systemId.endsWith(XSD_SUFFIX)) {
                    return this.schemaResolver.resolveEntity(publicId, systemId);
                }
            }
    
            // Fall back to the parser's default behavior.
            return null;
        }
    
    }

       没什么特殊的处理,就是直接通过 systemId 的后缀来进行不同类型的解析委托。

    3.1.4、ResourceEntityResolver

      org.springframework.beans.factory.xml.ResourceEntityResolver :继承自 DelegatingEntityResolver ,先委托父类 DelegatingEntityResolver 进行解析,如果失败,则通过 resourceLoader 进行加载,甚至是直接打开 URL 流进行加载。具体实现如下:

    @Override
    @Nullable
    public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId)
            throws SAXException, IOException {
    
        // 先委托父类 DelegatingEntityResolver 的方法进行解析
        InputSource source = super.resolveEntity(publicId, systemId);
    
        // 解析失败,使用 resourceLoader 进行解析
        if (source == null && systemId != null) {
            String resourcePath = null;
            try {
                // 使用 UTF-8 解码 systemId
                String decodedSystemId = URLDecoder.decode(systemId, "UTF-8");
                // 转换成 URL 字符串
                String givenUrl = new URL(decodedSystemId).toString();
                // 解析文件资源的相对路径(相对于系统根路径)
                String systemRootUrl = new File("").toURI().toURL().toString();
                // Try relative to resource base if currently in system root.
                if (givenUrl.startsWith(systemRootUrl)) {
                    resourcePath = givenUrl.substring(systemRootUrl.length());
                }
            }
            catch (Exception ex) {
                // Typically a MalformedURLException or AccessControlException.
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve XML entity [" + systemId + "] against system root URL", ex);
                }
                // No URL (or no resolvable URL) -> try relative to resource base.
                resourcePath = systemId;
            }
            if (resourcePath != null) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Trying to locate XML entity [" + systemId + "] as resource [" + resourcePath + "]");
                }
                // 通过 resourceLoader 获取资源
                Resource resource = this.resourceLoader.getResource(resourcePath);
                // 创建 InputSource 实例对象,并设置 publicId 和 systemId 属性
                source = new InputSource(resource.getInputStream());
                source.setPublicId(publicId);
                source.setSystemId(systemId);
                if (logger.isDebugEnabled()) {
                    logger.debug("Found XML entity [" + systemId + "]: " + resource);
                }
            }
            else if (systemId.endsWith(DTD_SUFFIX) || systemId.endsWith(XSD_SUFFIX)) {
                // External dtd/xsd lookup via https even for canonical http declaration
                // 将 http 声明转成 https
                String url = systemId;
                if (url.startsWith("http:")) {
                    url = "https:" + url.substring(5);
                }
                try {
                    // 通过构造 URL 来获取 InputSource 实例对象,并设置 publicId 和 systemId 属性
                    source = new InputSource(new URL(url).openStream());
                    source.setPublicId(publicId);
                    source.setSystemId(systemId);
                }
                catch (IOException ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve XML entity [" + systemId + "] through URL [" + url + "]", ex);
                    }
                    // Fall back to the parser's default behavior.
                    source = null;
                }
            }
        }
    
        return source;
    }

       接下来我们来验证下错误引用时是很么情况,XML 配置如下 ( cn/wpbxin/spring-xmlbeans.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-beans2.xsd">
    
    </beans>

       读取的代码如下: 

    package cn.wpbxin.bean;
    
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    
    public class XmlBeanDefinitionReaderTest {
       public static void main(String[] args) {
          Resource resource = new ClassPathResource("cn/wpbxin/bean/spring-xmlbeans.xml");  // (1.1)
          // ClassPathResource resource = new ClassPathResource("beans.xml");  // (1.2)
          // Resource[] resources = PathMatchingResourcePatternResolver.getResources(locationPattern);  // (1.3),需要遍历获取 BeanDefinition
          DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
          XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
          reader.loadBeanDefinitions(resource);
       }
    
    }

      最终可发现是在 source = new InputSource(new URL(url).openStream()); 这里就出问题了,因为确实连接不上 'http://www.springframework.org/schema/beans/spring-beans2.xsd' 。报错日志如下:

    3.2、自定义 EntityResolver

      前面介绍 EntityResolver 时已经举过例子了,主要就是重写 EntityResolver.resolveEntity(String publicId, String systemId) 方法,自定义解析过程,此不赘述。

    3.3、其他说明

      Doument 实例的获取是常规的 SAX 解析 XML 文档。后面着重介绍的 EntityResolver ,其实是 自定义如何寻找【DTD/XSD 验证文件】的逻辑 ,这样就大概知道了配置文件中开头的 xsd 声明的作用了。

    4、参考

    加载BeanDefinition:

  • 相关阅读:
    METHODS OF AND APPARATUS FOR USING TEXTURES IN GRAPHICS PROCESSING SYSTEMS
    Display controller
    Graphics processing architecture employing a unified shader
    Graphics-Processing Architecture Based on Approximate Rendering
    Architectures for concurrent graphics processing operations
    Procedural graphics architectures and techniques
    DYNAMIC CONTEXT SWITCHING BETWEEN ARCHITECTURALLY DISTINCT GRAPHICS PROCESSORS
    Thermal zone monitoring in an electronic device
    System and method for dynamically adjusting to CPU performance changes
    Framework for Graphics Animation and Compositing Operations
  • 原文地址:https://www.cnblogs.com/wpbxin/p/13436741.html
Copyright © 2011-2022 走看看