resolveEntity(@Nullable String publicId, @Nullable String systemId) 方法,获取命名空间对应的 DTD 或 XSD 文件,方法如下:
// DelegatingEntityResolver.java @Override @Nullable public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException { if (systemId != null) { // DTD 模式 if (systemId.endsWith(DTD_SUFFIX)) { return this.dtdResolver.resolveEntity(publicId, systemId); } // XSD 模式 else if (systemId.endsWith(XSD_SUFFIX)) { return this.schemaResolver.resolveEntity(publicId, systemId); } } // Fall back to the parser's default behavior. return null; } // ResourceEntityResolver.java @Override @Nullable public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException { // <1> 调用父类的方法,进行解析,获取本地 XSD 文件资源 InputSource source = super.resolveEntity(publicId, systemId); // <2> 如果没有获取到本地 XSD 文件资源,则尝试通直接通过 systemId 获取(网络形式) if (source == null && systemId != null) { // <2.1> 将 systemId 解析成一个 URL 地址 String resourcePath = null; try { String decodedSystemId = URLDecoder.decode(systemId, "UTF-8"); 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; } // <2.2> 如果 URL 地址解析成功,则根据该地址获取对应的 Resource 文件资源 if (resourcePath != null) { if (logger.isTraceEnabled()) { logger.trace("Trying to locate XML entity [" + systemId + "] as resource [" + resourcePath + "]"); } // 获得 Resource 资源 Resource resource = this.resourceLoader.getResource(resourcePath); // 创建 InputSource 对象 source = new InputSource(resource.getInputStream()); // 设置 publicId 和 systemId 属性 source.setPublicId(publicId); source.setSystemId(systemId); if (logger.isDebugEnabled()) { logger.debug("Found XML entity [" + systemId + "]: " + resource); } } // <2.3> 否则,再次尝试直接根据 systemId(如果是 "http" 则会替换成 "https")获取 XSD 文件(网络形式) else if (systemId.endsWith(DTD_SUFFIX) || systemId.endsWith(XSD_SUFFIX)) { // External dtd/xsd lookup via https even for canonical http declaration String url = systemId; if (url.startsWith("http:")) { url = "https:" + url.substring(5); } try { 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; }过程如下:
调用父类的方法,进行解析,获取本地 XSD 文件资源,如果是 XSD 模式,则先通过 PluggableSchemaResolver 解析
如果没有获取到本地 XSD 文件资源,则尝试通直接通过 systemId 获取(网络形式)
将 systemId 解析成一个 URL 地址
如果 URL 地址解析成功,则根据该地址获取对应的 Resource 文件资源
否则,再次尝试直接根据 systemId(如果是 "http" 则会替换成 "https")获取 XSD 文件(网络形式)
先尝试获取本地的 XSD 文件,获取不到再获取远程的 XSD 文件
PluggableSchemaResolverorg.springframework.beans.factory.xml.PluggableSchemaResolver,获取 XSD 文件(网络形式)对应的本地的文件资源
构造函数 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; /** Schema 文件地址 */ private final String schemaMappingsLocation; /** Stores the mapping of schema URL -> local schema path. */ @Nullable private volatile Map<String, String> schemaMappings; public PluggableSchemaResolver(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; } }注意这里的 DEFAULT_SCHEMA_MAPPINGS_LOCATION 为 META-INF/spring.schemas,看到这个可以确定实现原理就在这里了
schemaMappingsLocation 属性默认为 META-INF/spring.schemas
resolveEntity 方法