SAX EntityResolver 的作用

1.1 何为 EntityResolver :

官方解释: 如果SAX应用程序叙事实现自定义处理外部实体,则必须实现此接口,

并使用setEntityResolver方法向SAX 驱动器注册一个实例.

也就是说,对于解析一个xml,sax

首先会读取该xml文档上的声明,根据声明去寻找相应的dtd定义,以便对文档的进行验证,
默认的寻找规则,(即:通过网络,实现上就是声明DTD的地址URI地址来下载DTD声明),
并进行认证,下载的过程是一个漫长的过程,而且当网络不可用时,这里会报错,就是应为相应的dtd没找到,

1.2 EntityResolver 的作用就是项目本身就可以提供一个如何寻找DtD 的声明方法,

即:由程序来实现寻找DTD声明的过程,比如我们将DTD放在项目的某处在实现时直接将此文档读取并返回个SAX即可,这样就避免了通过网络来寻找DTD的声明

1.3 首先看看EntityResolver 接口声明的方法.

 

  public abstract InputSource resolveEntity (String publicId,
                                               String systemId)
        throws SAXException, IOException;

 

这里,他接收2个参数,publicId ,systemId ,并返回一个InputStream对象,
这里我们以特定的文件来讲解;

1.3.1  如果我们在解析验证模式为xsd的配置文件,代码如下 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
...
</beans> 

读取得到以下参数,

 publicId : null

 systemId : http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

1.3.2 如果我们解析的是DTD的配置文件;

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
        ...
</beans>

读取得到以下参数:

publicId : -//SPRING//DTD BEAN//EN

systemId : http://www.springframework.org/dtd/spring-beans.dtd

之前已经提到过,默认的寻找规则,(即:通过网络,实现上就是声明DTD的地址URI地址来下载DTD声明),这样才会造成延迟,用户体验也不好,一般的做法是将验证文件放在自己的工程里面,

那么怎么做才能将这个Url转换为自己工程里对应的文件呢?我们已加载DTD文件为例看看Spring中是如通过getEntityResolver() 对  EntityResolver 的获取,

1.4  DelegatingEntityResolver

我们知道Spring中使用DelegatingEntityResolver 类为 EntityResolver的实现类,

 EntityResolver 的实现方法如下

 

 1 @Override
 2     public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
 3         if (systemId != null) {
 4             // 如果是DTD从这里开始
 5             if (systemId.endsWith(DTD_SUFFIX)) {
 6                 return this.dtdResolver.resolveEntity(publicId, systemId);
 7             }
 8             // 如果是XSD从这里开始
 9             else if (systemId.endsWith(XSD_SUFFIX)) {
10                 // 通过调用META-INF/Spring.schemas解析
11                 return this.schemaResolver.resolveEntity(publicId, systemId);
12             }
13         }
14         return null;
15     }

 

我们可以看到,对不同的验证模式,Spring用了不同的解析器,这里简单描述一下原理,

1.4.1  BeanDtdResolver 

比如加载DTD类型的 BeanDtdResolver 的 resolveEntity是直接截取systemId最后的xxx.dtd ,然后去当前路径下寻找,代码如下:

 1 @Override
 2     public InputSource resolveEntity(String publicId, String systemId) throws IOException {
 3         if (logger.isTraceEnabled()) {
 4             logger.trace("Trying to resolve XML entity with public ID [" + publicId +
 5                     "] and system ID [" + systemId + "]");
 6         }
 7         //截取systemId最后的xxx.dtd
 8         if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
 9             int lastPathSeparator = systemId.lastIndexOf("/");
10             for (String DTD_NAME : DTD_NAMES) {
11                 int dtdNameStart = systemId.indexOf(DTD_NAME);
12                 if (dtdNameStart > lastPathSeparator) {
13                     String dtdFile = systemId.substring(dtdNameStart);
14                     if (logger.isTraceEnabled()) {
15                         logger.trace("Trying to locate [" + dtdFile + "] in Spring jar");
16                     }
17                     try {
18                         Resource resource = new ClassPathResource(dtdFile, getClass());
19                         InputSource source = new InputSource(resource.getInputStream());
20                         source.setPublicId(publicId);
21                         source.setSystemId(systemId);
22                         if (logger.isDebugEnabled()) {
23                             logger.debug("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);
24                         }
25                         return source;
26                     }
27                     catch (IOException ex) {
28                         if (logger.isDebugEnabled()) {
29                             logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in class path", ex);
30                         }
31                     }
32 
33                 }
34             }
35         }

 

1.4.2 PluggableSchemaResolver 

而加载XSD类型的PluggableSchemaResolver 类的 resolveEntity 是默认到META-INF/Spring.schemas文件中找到systemid 对应的XSD文件并加载代码如下

 1 @Override
 2     public InputSource resolveEntity(String publicId, String systemId) throws IOException {
 3         if (logger.isTraceEnabled()) {
 4             logger.trace("Trying to resolve XML entity with public id [" + publicId +
 5                     "] and system id [" + systemId + "]");
 6         }
 7 
 8         if (systemId != null) {
 9             //获取 systemId 对应的XSD文件
10             String resourceLocation = getSchemaMappings().get(systemId);
11             if (resourceLocation != null) {
12                 Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
13                 try {
14                     InputSource source = new InputSource(resource.getInputStream());
15                     source.setPublicId(publicId);
16                     source.setSystemId(systemId);
17                     if (logger.isDebugEnabled()) {
18                         logger.debug("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
19                     }
20                     return source;
21                 }
22                 catch (FileNotFoundException ex) {
23                     if (logger.isDebugEnabled()) {
24                         logger.debug("Couldn't find XML schema [" + systemId + "]: " + resource, ex);
25                     }
26                 }
27             }
28         }
29         return null;
30     }

 

 

posted @ 2014-04-03 13:48  mjorcen  阅读(4919)  评论(0编辑  收藏  举报