Spring中运用 SAX的 EntityResolver

目录

1、EntityResolver 介绍

XSD

DTD

2、Spring源码中类图

DTD方式

XSD 方式


1、EntityResolver 介绍

官网解释:如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用 setEntityResolver 方法 向 SAX 驱动器注册一个实例。

对于 XML 文档的解析,SAX首先读取文档声明,根据文档声明去找 DTD 或 XSD ,然后对文档进行验证。默认的寻找规则,是先通过网络来下载 DTD 或XSD,然而网络是不可靠的,所以需要项目本身提供一个获取 DTD或XSD的方式。

EntityResolver 的作用就是自定义获取 DTD或 XSD的方式。

EntityResolver 源码:

package org.xml.sax;

import java.io.IOException;

public interface EntityResolver {

    public abstract InputSource resolveEntity (String publicId,
                                               String systemId)
        throws SAXException, IOException;

}

接收两个参数:publicId,systemId。

XSD

配置文件如下:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

则 publicId 为 null,systemId为:https://www.springframework.org/schema/beans/spring-beans.xsd

DTD

<?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>

publicId:-//Spring//DTD BEAN 2.0//EN

systemIdhttp://www.springframework.org/dtd/spring-beans-2.0.dtd

 

2、Spring源码中类图

其中 EntityResolver 是 org.xml.sax.EntityResolver ,SAX解析XML包的类。

这里实现类 DelegatingEntityResolver 是核心类,源码如下

package org.springframework.beans.factory.xml;

import java.io.IOException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class DelegatingEntityResolver implements EntityResolver {
    public static final String DTD_SUFFIX = ".dtd";
    public static final String XSD_SUFFIX = ".xsd";
    private final EntityResolver dtdResolver;
    private final EntityResolver schemaResolver;

    // 构造函数
    public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
        // 加载DTD文件,文中继续介绍
        this.dtdResolver = new BeansDtdResolver();
        // 加载XSD文件,文中继续介绍
        this.schemaResolver = new PluggableSchemaResolver(classLoader);
    }

    public DelegatingEntityResolver(EntityResolver dtdResolver, EntityResolver schemaResolver) {
        Assert.notNull(dtdResolver, "'dtdResolver' is required");
        Assert.notNull(schemaResolver, "'schemaResolver' is required");
        this.dtdResolver = dtdResolver;
        this.schemaResolver = schemaResolver;
    }

    @Nullable
    public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException {
        if(systemId != null) {
            // DTD 方式
            if(systemId.endsWith(".dtd")) {
                return this.dtdResolver.resolveEntity(publicId, systemId);
            }
            // XSD 方式
            if(systemId.endsWith(".xsd")) {
                return this.schemaResolver.resolveEntity(publicId, systemId);
            }
        }

        return null;
    }

    public String toString() {
        return "EntityResolver delegating .xsd to " + this.schemaResolver + " and " + ".dtd" + " to " + this.dtdResolver;
    }
}

DTD方式

BeansDtdResolver 源码,源码内有注释:主要是截取dtd尾部文件名,在类BeansDtdResolver 同目录下获取dtd文件。

同目录下dtd 如下图:

BeansDtdResolver 源码如下(内涵注释):

package org.springframework.beans.factory.xml;

import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

public class BeansDtdResolver implements EntityResolver {
    private static final String DTD_EXTENSION = ".dtd";
    private static final String DTD_NAME = "spring-beans";
    private static final Log logger = LogFactory.getLog(BeansDtdResolver.class);

    public BeansDtdResolver() {
    }

    @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 + "]");
        }
        // systemId = http://www.springframework.org/dtd/spring-beans-2.0.dtd
        if(systemId != null && systemId.endsWith(".dtd")) {
            // ASCII码表,47 对应字符 /
            // 获取最后一个斜杠位置,判断尾部有没有spring-beans
            int lastPathSeparator = systemId.lastIndexOf(47);
            int dtdNameStart = systemId.indexOf("spring-beans", lastPathSeparator);
            if(dtdNameStart != -1) {
                String dtdFile = "spring-beans.dtd";
                if(logger.isTraceEnabled()) {
                    logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
                }

                try {
                    // 在此类(BeansDtdResolver)同目录下获取 spring-beans.dtd 
                    Resource resource = new ClassPathResource(dtdFile, this.getClass());
                    // 包装后返回
                    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 var8) {
                    if(logger.isDebugEnabled()) {
                        logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", var8);
                    }
                }
            }
        }

        return null;
    }

    public String toString() {
        return "EntityResolver for spring-beans DTD";
    }
}

 

XSD 方式

PluggableSchemaResolver源码,默认到 META-INF/spring.schemas 文件中找 systemId对应的 XSD文件加载。

源码如下

package org.springframework.beans.factory.xml;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

public class PluggableSchemaResolver implements EntityResolver {
    public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
    private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class);
    @Nullable
    private final ClassLoader classLoader;
    private final String schemaMappingsLocation;
    @Nullable
    private volatile Map<String, String> schemaMappings;

    public PluggableSchemaResolver(@Nullable ClassLoader classLoader) {
        this.classLoader = classLoader;
        this.schemaMappingsLocation = "META-INF/spring.schemas";
    }

    public PluggableSchemaResolver(@Nullable ClassLoader classLoader, String schemaMappingsLocation) {
        Assert.hasText(schemaMappingsLocation, "'schemaMappingsLocation' must not be empty");
        this.classLoader = classLoader;
        this.schemaMappingsLocation = schemaMappingsLocation;
    }

    @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) {
            String resourceLocation = (String)this.getSchemaMappings().get(systemId);
            if(resourceLocation == null && systemId.startsWith("https:")) {
                resourceLocation = (String)this.getSchemaMappings().get("http:" + systemId.substring(6));
            }

            if(resourceLocation != null) {
                ClassPathResource resource = new ClassPathResource(resourceLocation, this.classLoader);

                try {
                    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 var6) {
                    if(logger.isDebugEnabled()) {
                        logger.debug("Could not find XML schema [" + systemId + "]: " + resource, var6);
                    }
                }
            }
        }

        return null;
    }

    private Map<String, String> getSchemaMappings() {
        Map<String, String> schemaMappings = this.schemaMappings;
        if(schemaMappings == null) {
            synchronized(this) {
                schemaMappings = this.schemaMappings;
                if(schemaMappings == null) {
                    if(logger.isTraceEnabled()) {
                        logger.trace("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
                    }

                    try {
                        Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
                        if(logger.isTraceEnabled()) {
                            logger.trace("Loaded schema mappings: " + mappings);
                        }

                        schemaMappings = new ConcurrentHashMap(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, (Map)schemaMappings);
                        this.schemaMappings = (Map)schemaMappings;
                    } catch (IOException var5) {
                        throw new IllegalStateException("Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", var5);
                    }
                }
            }
        }

        return (Map)schemaMappings;
    }

    public String toString() {
        return "EntityResolver using schema mappings " + this.getSchemaMappings();
    }
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值