2.7 获取Document
之前我们探讨经过获取完了XML的验证模式之后,就会加载XML文件,获取对应的Doucment对象。之间代码追溯的了
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {1
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
XmlBeanFactoryReader对于获取document就没有亲自亲为了,this.documentLoader其实是DefaultDocumentLoader类执行的loadDocument() 在这里的方法返回的document,看看这个方法怎么返回的一个document
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);
}
这部分的代码也不用描述太多的内容,因为是通过SAX解析的XML文档套路大致都差不多,同样首先创建一个DocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource来返回Document对象。SAX解析XML的步骤:1. 获取SAX解析器工厂的实例 2. 获取SAX解析器的实例 3. 解析调用parse方法(File\InputSreme)
2.7.1EntityResolver的用法
SAX解析一个Xml,首先是获取该XML文档上的声明,根据声明寻找相应的DTD声明,并且进行认证。默认的寻找规则是通过网络(实际上通过声明的DTD的URI地址)来下载相应的DTD声明,并且进行认证。下载过程可能很漫长并且容易出错,而且网络中断的情况下,这里会报错。所以......
EntityResolver作用:让项目本身就可以提供给一个如何寻找DTD声明的方法,即由程序寻找DTD声明的过程,比如我们将DTD声明放在项目中的某一个位置,在实现时直接将此文档读取并且返回给SAX即可。这样就避免了通过网络来寻找相应的声明。
entityResolver接口的声明的方法 InputSource resolveEntity(String publicId,String systemsId)
我们在解析验证模式的时候,配置文件的XSD与DTD的两个参数不同。我们看一下entityResolver接口的实现方法
public InputSource resolveEntity(String publicId, @Nullable 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使用了不同的解析器来解析。这里简单的描述一下原理。比如加载DTD类型的时候是通过BeansDtdResolver类截取systemId最后的xx.dtd,然后去当前路径下寻找。
public class BeansDtdResolver implements EntityResolver {
private static final String DTD_EXTENSION = ".dtd";
private static final String DTD_NAME = "spring-beans";
private static final Log logger = LogFactory.getLog(BeansDtdResolver.class);
@Override
@Nullable
public InputSource resolveEntity(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 + "]");
}
if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
int lastPathSeparator = systemId.lastIndexOf('/');
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());
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isTraceEnabled()) {
logger.trace("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 classpath", ex);
}
}
}
}
// Use the default behavior -> download from website or wherever.
return null;
}
@Override
public String toString() {
return "EntityResolver for spring-beans DTD";
}
}
而加载XSD类型的PluggableSchemaResolver类默认到META-INF/Spring.schemas文件中找到的systemid所对应的XSD文件来加载
public InputSource resolveEntity(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 + "]");
}
if (systemId != null) {
String resourceLocation = getSchemaMappings().get(systemId);
if (resourceLocation != null) {
Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
try {
InputSource source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
if (logger.isTraceEnabled()) {
logger.trace("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
}
return source;
}
catch (FileNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find XML schema [" + systemId + "]: " + resource, ex);
}
}
}
}
return null;
}