在上篇spring容器实现之获取Document实例上中DefaultDocumentLoader#loadDocument()方法中,遗留了一个问题,参数EntityResolver,何为EntityResolver?官方给出了一个这样的解释:
如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法想SAX驱动器注册一个实例,详细点说,就是对于解析一个XML,SAX首先读取该xml的文档的声明,在然后根据申明去寻找相应的DTD定义.
其次EntityResolver本身就提供了一个如何寻找DTD的方法,实质上是由程序去寻找.该扯的也扯了,接下来看正题:
public abstract InputSource resolveEntity (String publicId,
String systemId)
throws SAXException, IOException;
上述方法是来自EntityResolver接口的方法,可以看到该方法只接受两个参数
publicId:是一个引用外部实体的公共的标识,允许可以为null
systemId:引用外部实体的系统标识.
最后我们可以发现该方法返回一个inputSource对象.接下来看一下它的实现类:
最终我们来到了xmlBeanDefinitionReader#getEntityResolver,代码如下:
/**返回一个解析器,如果没有指定则构建一个*/
protected EntityResolver getEntityResolver() {
//当前没有指定解析器
if (this.entityResolver == null) {
//确认使用默认的
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
上面方法就是获取一个EntityResolver解析器,简单的说一下:
- 首先判断是否有指定的解析器供使用,显然在代码中是没有的.
- 没有指定的解析器使用,需要使用默认的,首先构建一个ResourceLoader加载器
- 构建的加载器如果不为null的情况下:
- 给ResourceEntityResolver设置属性(ResourceLoader).
-为null的情况下: - 首先获取一个资源加载器,然后设置DelegatingEntityResolver.
- 最后返回默认的解析器
ResourceLoader的构建过程
跟踪代码来到AbstractBeanDefinitionReader类中:
private ResourceLoader resourceLoader;
public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
代码简单,无需多解释,接着看如果ResourceLoader不为null的情况下:
跟踪代码我们来到DelegatingEntityResolver(这是一个代理解析器),看代码:
''''''''
/** Suffix for DTD files. */
public static final String DTD_SUFFIX = ".dtd";
/** Suffix for schema definition files. */
public static final String XSD_SUFFIX = ".xsd";
//文档类型的解析器
private final EntityResolver dtdResolver;
//schema类型的解析器
private final EntityResolver schemaResolver;
/***
* 通过类加载器构建不同的解析器
* @param classLoader
*/
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
//构建加载DTD文档的加载器
this.dtdResolver = new BeansDtdResolver();
//构建XSD类型的解析器
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
上述代码主要是通过DelegatingEntityResolver去实现解析器的获取,具体是那种得看spring是如何调配的了,上面代码分别构建了不同类型的解析器,分别来看:
DTD解析器
我们发现spring是通过BeansDtdResolver()来构建的跟踪代码进去:
''''''
public class BeansDtdResolver implements EntityResolver {
//文档末尾以.dtd
private static final String DTD_EXTENSION = ".dtd";
//文档的开头是spring-beans的
private static final String DTD_NAME = "spring-beans";
private static final Log logger = LogFactory.getLog(BeansDtdResolver.class);
@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 + "]");
}
//文档的标识以.dtd结尾
if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
//从后往前找最后一个以/开头的元素
int lastPathSeparator = systemId.lastIndexOf('/');
//从前往后找获取以上面结尾且name为DTD_NAME的元素
int dtdNameStart = systemId.indexOf(DTD_NAME, lastPathSeparator);
if (dtdNameStart != -1) {
//拼接
String dtdFile = DTD_NAME + DTD_EXTENSION;
if (logger.isTraceEnabled()) {
logger.trace("Trying to locate [" + dtdFile + "] in Spring jar on classpath");
}
try {
//封装
Resource resource = new ClassPathResource(dtdFile, getClass());
InputSource source = new InputSource(resource.getInputStream());
//设置相应publicId和systemId属性
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isTraceEnabled()) {
logger.trace("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
}
return source;
}
catch (FileNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in classpath", ex);
}
}
}
}
// Fall back to the parser's default behavior.
return null;
}
从代码中发现,原来DTD加载的解析器交给了BeansDtdResolver#resolveEntity方法来处理,解析过程很明确,可以发现的是在解析的过程中,是直接通过systemId直接去截取最后的.dtd结尾然后去当前路径下寻找,这里涉及到了systemId和publicId的构造,简单的来看一下各自的定义:
DTD的模式
publicId:-//SPRING//DTD BEAN 2.0//EN
systemId:[http://www.springframework.org/dtd/spring-beans.dtd](http://www.springframework.org/dtd/spring-beans.dtd)模式模式
XSD模式
publicId:null
systemId:[http://www.springframework.org/schema/beans/spring-beans.xsd](http://www.springframework.org/schema/beans/spring-beans.xsd)
XSD解析器
关于XSD的解析器是如何实现的直接看代码:
/**默认的XSD映射路径*/
public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
@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 = DEFAULT_SCHEMA_MAPPINGS_LOCATION;
}
该PluggableSchemaResolver类中还有几个方法,想看的可以自己看看源码这里就不在解释了.......
转载于:https://www.jianshu.com/p/ede95b41478f