通过了验证模式准备的不走就可以进行Document加载了。同样XmlBeanFactoryReader类对于文档读取并没有亲力亲为,而是委托了DocumentLoader去执行。这里的DocumentLoader是个借口。而真正调用的是DefaultDocumentLoader。
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);
}
这段代码通过SAX解析XML文档。首先创建DocumentBuiderFactory,在通过DocumentBuilderFactory创建DocumentBuilder。进行解析inputSource来返回Document对象。
在loadDocument方法中涉及一个参数EntityResolver,那什么是EntityResovler呢?
EntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,既有程序来实现寻找DTD声明的过程。比如我们将DTD文件放在项目中的某处。在实现时直接将此文档读取返回给sax即可。这样就避免了通过网络来寻找相应的声明。
首先看entityResolver的接口方法声明:
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;
}
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId != null) {
if (systemId.endsWith(DTD_SUFFIX)) {
return this.dtdResolver.resolveEntity(publicId, systemId);
}
else if (systemId.endsWith(XSD_SUFFIX)) {
return this.schemaResolver.resolveEntity(publicId, systemId);
}
}
return null;
}
对于不同的验证模式,Spring使用了不同的解析器解析。
public InputSource resolveEntity(String publicId, 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 && systemId.endsWith(DTD_EXTENSION)) {
int lastPathSeparator = systemId.lastIndexOf("/");
for (String DTD_NAME : DTD_NAMES) {
int dtdNameStart = systemId.indexOf(DTD_NAME);
if (dtdNameStart > lastPathSeparator) {
String dtdFile = systemId.substring(dtdNameStart);
if (logger.isTraceEnabled()) {
logger.trace("Trying to locate [" + dtdFile + "] in Spring jar");
}
try {
Resource resource = new ClassPathResource(dtdFile, getClass());
InputSource source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isDebugEnabled()) {
logger.debug("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
}
return source;
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in class path", ex);
}
}
}
}
}
// Use the default behavior -> download from website or wherever.
return null;
}
加载DTD类型的beansDtdResolver的resolveEntity是直接截取systemId最后的xx.dtd然后去当前路径下寻找,而加载xsd类型的pluggableschemaResolver类resolveEntity是默认到META-INF/Spring.schemas文件中找到systemid所对应的xsd文件加载。