死磕Spring之IoC篇 - 解析自定义标签(XML 文件) (7)

resolveEntity(@Nullable String publicId, @Nullable String systemId) 方法,获取命名空间对应的 DTD 或 XSD 文件(本地),方法如下:

@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) { // <1> 获得对应的 XSD 文件位置,从所有 `META-INF/spring.schemas` 文件中获取对应的本地 XSD 文件位置 String resourceLocation = getSchemaMappings().get(systemId); if (resourceLocation == null && systemId.startsWith("https:")) { // Retrieve canonical http schema mapping even for https declaration resourceLocation = getSchemaMappings().get("http:" + systemId.substring(6)); } if (resourceLocation != null) { // 本地 XSD 文件位置 // <2> 创建 ClassPathResource 对象 Resource resource = new ClassPathResource(resourceLocation, this.classLoader); try { // <3> 创建 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; }

过程如下:

获得对应的 XSD 文件位置 resourceLocation,从所有 META-INF/spring.schemas 文件中获取对应的本地 XSD 文件位置,会先调用 getSchemaMappings() 解析出本地所有的 XSD 文件的位置信息

根据 resourceLocation 创建 ClassPathResource 对象

创建 InputSource 对象,设置 publicId、systemId 属性,返回

getSchemaMappings 方法

getSchemaMappings()方法, 解析当前 JVM 环境下所有的 META-INF/spring.handlers 文件的内容,方法如下:

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`,也就是当前 JVM 环境下所有的 `META-INF/spring.handlers` 文件的内容都会读取到 Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded schema mappings: " + mappings); } // 将 mappings 初始化到 schemaMappings 中 schemaMappings = new ConcurrentHashMap<>(mappings.size()); 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; }

逻辑不复杂,会读取当前 JVM 环境下所有的 META-INF/spring.schemas 文件,将里面的内容以 key-value 的形式保存在 Map 中返回,例如保存如下信息:

key=http://www.springframework.org/schema/context/spring-context.xsd value=org/springframework/context/config/spring-context.xsd

这样一来,会先获取本地 org/springframework/context/config/spring-context.xsd 文件,不存在则尝试获取 文件,避免无网情况下无法获取 XSD 文件

自定义标签实现示例

例如我们有一个 User 实例类和一个 City 枚举:

package org.geekbang.thinking.in.spring.ioc.overview.domain; import org.geekbang.thinking.in.spring.ioc.overview.enums.City; public class User implements BeanNameAware { private Long id; private String name; private City city; // ... 省略 getter、setter 方法 } package org.geekbang.thinking.in.spring.ioc.overview.enums; public enum City { BEIJING, HANGZHOU, SHANGHAI } 编写 XML Schema 文件(XSD 文件)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwdfxz.html