在上篇文章IoC容器源码-容器的基础的末尾,我们讲到了“加载Xml文件,并封装成Document 实例”是通过Document doc = doLoadDocument(inputSource, resource);
来完成的。本文将详细讲解doLoadDocument方法是如何获取Document的。
org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadDocument(InputSource, Resource)
/**
* Actually load the specified document using the configured DocumentLoader.
* ...
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
可以看到,获取Document实例是依赖documentLoader.loadDocument
方法完成的。在看这个方法之前,先来看下getEntityResolver方法、getValidationModeForResource方法和isNamespaceAware方法的简单介绍。
准备工作
getEntityResolver()方法
/**
* Return the EntityResolver to use, building a default resolver
* if none specified.
*/
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;
}
可以看到,这个方法用来返回一个EntityResolver类型的对象。EntityResolver又是做什么的呢?
官方文档称,如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动注册一个实例。
对于解析一个XML,SAX应用程序首先读取该XML文档上的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个验证。默认的寻找规则,即通过网络(实现上就是声明的DTD的URL地址)来下载相应的dtd声明,并进行认证。下载的过程是一个漫长的过程,而且当网络中断或不可用时,这里就会报错的,就是因为相应的DTD声明没有被找到的原因。
EntityResolver的作用就是项目本身就可以提供一个如何寻找dtd声明的方法,即由程序来实现寻找dtd声明的过程,比如我们将dtd文件放到项目中某处,在实现时直接将此文档读取并返回给sax,即可。这样就避免了通过网络来寻找相应的声明。
getValidationModeForResource方法
/**
* Gets the validation mode for the specified {@link Resource}. If no explicit
* validation mode has been configured then the validation mode is
* {@link #detectValidationMode detected}.
* <p>Override this method if you would like full control over the validation
* mode, even when something other than {@link #VALIDATION_AUTO} was set.
*/
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
return VALIDATION_XSD;
}
这个方法用来读取XML文件的验证模式。XML的验证模式保证了XML文件的正确性。常用的验证模式有DTD何XSD两种。关于XML的验证模式以后会详细讲解。
isNamespaceAware方法
/**
* Return whether or not the XML parser should be XML namespace aware.
*/
public boolean isNamespaceAware() {
return this.namespaceAware;
}
这个方法用来返回XML解析器是否应该是XML命名空间知道的,this.namespaceAware默认值为false。如果为true,则XML命名空间必须提供。
loadDocument方法
org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument(InputSource, EntityResolver, ErrorHandler, int, boolean)
/**
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode,
namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
方法分为三步:
- 创建DocumentBuilderFactory对象
- 创建DocumentBuilder对象
- 通过DocumentBuilder对象解析inputSource并返回。
DocumentBuilder是个抽象类,public abstract class DocumentBuilder。parse方法的具体实现为
org.apache.xerces.jaxp.DocumentBuilderImpl.parse(InputSource)
public Document parse(InputSource is) throws SAXException, IOException {
if (is == null) {
throw new IllegalArgumentException(
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"jaxp-null-input-source", null));
}
if (fSchemaValidator != null) {
if (fSchemaValidationManager != null) {
fSchemaValidationManager.reset();
fUnparsedEntityHandler.reset();
}
resetSchemaValidator();
}
domParser.parse(is);
Document doc = domParser.getDocument();
domParser.dropDocumentReferences();
return doc;
}