EntityResolver的实现类
友情提示:下面的代码可能很多,会劝退一些人,大家只要着重看我注释的地方就可以,因为如果看源码,要深究每一行代码,那么几乎就无法完成了,需要的是理解spring的思路以及重点的地方。
- ResourceEntityResolver
- 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系列的文章,有看过的,或者是想看的可以关注一下,一起讨论。