XML解析为Document对象

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的相关接口,这里我们就不详细来讲了

    原文作者:久安0529
    原文地址: https://blog.csdn.net/a5799/article/details/115680435
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞