理解EntityResolver在Spring加载Bean中的作用

EntityResolver的实现类

友情提示:下面的代码可能很多,会劝退一些人,大家只要着重看我注释的地方就可以,因为如果看源码,要深究每一行代码,那么几乎就无法完成了,需要的是理解spring的思路以及重点的地方。
  1. ResourceEntityResolver
  2. DelegatingEntityResolver
    共有两个实现类,而ResourceEntityResolver继承了DelegatingEntityResolver类,并重写了resolveEntity方法。
    ResourceEntityResolver的resolveEntity方法代码如下:
public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException {
		// 这里的super.resolveEntity(DelegatingEntityResolver)方法划重点,这也是本类的最终作用,由于验证XML文件正常需要通过网络去访问网址,不理解的可以去搜索一下哈,很简单的,这里不再阐述,由于网络等原因带来的不良好体验,所以Spring建立了如图1.1的文件供验证使用
        InputSource source = super.resolveEntity(publicId, systemId);
        // 下面的代码块我认为是,由于spring为了预防加载失败时,使用网络的方式进行验证,有兴趣同学可以看一下。
        if (source == null && systemId != null) {
            String resourcePath = null;

            String url;
            try {
                url = URLDecoder.decode(systemId, "UTF-8");
                String givenUrl = (new URL(url)).toString();
                String systemRootUrl = (new File("")).toURI().toURL().toString();
                if (givenUrl.startsWith(systemRootUrl)) {
                    resourcePath = givenUrl.substring(systemRootUrl.length());
                }
            } catch (Exception var9) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not resolve XML entity [" + systemId + "] against system root URL", var9);
                }

                resourcePath = systemId;
            }

            if (resourcePath != null) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Trying to locate XML entity [" + systemId + "] as resource [" + resourcePath + "]");
                }

                Resource resource = this.resourceLoader.getResource(resourcePath);
                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") || systemId.endsWith(".xsd")) {
                url = systemId;
                if (systemId.startsWith("http:")) {
                    url = "https:" + systemId.substring(5);
                }

                try {
                    source = new InputSource((new URL(url)).openStream());
                    source.setPublicId(publicId);
                    source.setSystemId(systemId);
                } catch (IOException var8) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve XML entity [" + systemId + "] through URL [" + url + "]", var8);
                    }

                    source = null;
                }
            }
        }

        return source;
    }
上面所提到的重点也就是父类中**resolveEntity**方法,父类在上面提到就是**DelegatingEntityResolver**
public class DelegatingEntityResolver implements EntityResolver {
    private final EntityResolver dtdResolver;
    private final EntityResolver schemaResolver;

    public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
        this.dtdResolver = new BeansDtdResolver();
        this.schemaResolver = new PluggableSchemaResolver(classLoader);
    }

    public DelegatingEntityResolver(EntityResolver dtdResolver, EntityResolver schemaResolver) {
        this.dtdResolver = dtdResolver;
        this.schemaResolver = schemaResolver;
    }

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

            if (systemId.endsWith(".xsd")) {
                return this.schemaResolver.resolveEntity(publicId, systemId);
            }
        }

        return null;
    }
}
上面的代码中为了看起来简洁,不让读者看着就不想看,对有一些验证等进行了简化,想看源代码的同学通过IDEA或者jar包反编译等自行查看。

验证XSD的代码解析

最重要的也就是resolveEntity方法,也就是进行验证的主处理方法,而对于两种XML,DTD和XSD也有不同的类进行处理,这里着重XSD的分析,剩下的留给读者自行消化吸收。

Spring默认使用加载XSD的类为this.schemaResolver = new PluggableSchemaResolver(classLoader);构造方法中可见。

schemaResolver.resolveEntity(publicId, systemId)源代码如下:

public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws IOException {
// 好奇getSchemaMappings()是什么嘛,既然注意到了这里就说明很棒,哈哈,XSD的systemId也就是网络路径,如http://www.springframework.org/schema/beans/spring-beans.xsd
       if (systemId != null) {
            String resourceLocation = (String)this.getSchemaMappings().get(systemId);
            //这里代码很简单,但是不得不提Spring的严谨,就是吹一波
            if (resourceLocation == null && systemId.startsWith("https:")) {
                resourceLocation = (String)this.getSchemaMappings().get("http:" + systemId.substring(6));
            }
			// 拿到文件路径后,获取文件的输入流,对XSD的验证准备工作完成。
            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;
    }

this.getSchemaMappings代码也就是真正的核心代码:

private Map<String, String> getSchemaMappings() {
        Map<String, String> schemaMappings = this.schemaMappings;
    if (schemaMappings == null) {
        synchronized(this) {
           schemaMappings = this.schemaMappings;
            if (schemaMappings == null) {
              try {
              // spring.schemas加载文件中的内容,内容中也就是网址路径对应相应的文件路径,例:http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
				Properties mappings = PropertiesLoaderUtils.loadAllProperties("META-INF/spring.schemas", this.classLoader);
			                       
				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;
    }

org/springframework/beans/factory/xml/spring-beans.xsd(加粗部分的为包名)的文件中定义了里面有什么标签,标签中定义了什么样的属性等。

总结

如果有人看了这篇博客,发现没看出来啥东西,那你是对的,他真的没什么,spring并不是所有的东西都很难,给自己点信心,不是你没理解,而是他真的只有这么简单。
EntityResolver的作用也就是为了避免网络带来的不良好体验,处理方法也就是通过spring.schemas去找到指定的文件去代替网络上的信息,本地IO比较与网络IO的稳定性还是很强大的,本篇博客的东西不难,甚至有人读源码的时候都不会注意到。抛砖引玉,最近准备持续更新spring系列的文章,有看过的,或者是想看的可以关注一下,一起讨论。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值