XML解析为Document对象
我们在上一篇Spring源码分析中有提到,Spring是将xml文件的InputStream转换为DOM树,然后在将DOM树解析转换为BeanDefinition从而注册bean,那么这一篇,我们就来介绍一些Spring如何将xml文件转换为Document
首选DOM解析XML文件的官方标准,DOM解析就是将整个XML文件转换为一个DOM节点树,然后通过遍历和查找节点来读取XML文件中定义的数据。
源码分析
Document doc = doLoadDocument(inputSource, resource);
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
可以看到解析xml文件的核心方法就是doLoadDocument(inputSource, resource);需要将xml文件对应的inputSource对象,以及存储需解析的xml相关信息的resource对象传入从而进行解析。在实际起作用的loadDocument的方法中有还有四个个参数是不需要我们传入的,他们分布代表解析文件的解析器,处理加载 Document 对象的过程的错误,XML的验证模式以及是否支持命名空间。在spring中存在两种验证模式分布是DTD和XSD,spring会根据你的xml文件获取到正确的模式进行赋值,一般我们使用的都是XSD模式。
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
如果 ResourceLoader 不为 null,则根据指定的 ResourceLoader 创建一个 ResourceEntityResolver。如果 ResourceLoader 为null,则创建 一个 DelegatingEntityResolver,该 Resolver 委托给默认的 BeansDtdResolver 和 PluggableSchemaResolver 。
那么这个EntityResolver到底是做上面用的呢?其实这个对象就是用来处理文件的验证方式的。
一般进行xml验证时会读取xml文档上的声明,并根据声明去运行相应的dtd定义,以便对文档进行验证,默认寻找规则去通过(即:通过网络,实现上就是声明DTD的地址URI地址来下载DTD声明),
并进行认证,下载的过程是一个漫长的过程,而且当网络不可用时,这里会报错,就是因为相应的dtd没找到,而 EntityResolver 的作用就是项目本身就可以提供一个如何寻找DTD 的声明方法,
即:由程序来实现寻找DTD声明的过程,比如我们将DTD放在项目的某处在实现时直接将此文档读取并返回,避免了通过网络来寻找DTD声明。
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
从源码中可以看出,解析xml文件主要为三步:
1、创建DocumentBuilderFactory对象
2、创建DocumentBuilder对象
3、将inputSource解析为Document对象
那么我们先来看第一步创建DocumentBuilderFactory对象
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
//获取DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
//如果开启xml验证的话,验证xml
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
//如果xml验证模式为XSD的话,则需强制指定由此代码生成的解析器将提供对XML名称空间的支持
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {
//创建DocumentBuilder对象
DocumentBuilder docBuilder = factory.newDocumentBuilder();
// 2、尝试设置entityResolver
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
// 3、尝试设置errorHandler
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
最后一步的builder.parse(inputSource);就是调用jdk的相关接口,这里我们就不详细来讲了