首页 > 代码库 > mybatis,Spring等工具对xml文件正确性的验证

mybatis,Spring等工具对xml文件正确性的验证

  我们知道mybatis或者spring都是使用xml文件作为配置文件,配置文件的格式都是定义在叫做.dtd或者.xsd文件中的,当工具在解析用户自己定义的xml文件的时候,如何才能知道用户自定义的文件是否正确的呢?我们不能在xml文件中乱写一些框架不认识的标签,比如在spring的xml文件中写如下<user>标签,毫无疑问会报错。那么框架是怎么来验证我们所写的标签是否正确的呢?

<user>
  <id>100</id>
</user>

  由于mybatis使用的是dom解析,利用JDK的dom解析API,如下:

 1     @Test
 2     public void docFactoryTest() throws Exception {
 3         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 4         factory.setValidating(true);
 5         factory.setNamespaceAware(false);
 6         factory.setIgnoringComments(true);
 7         factory.setIgnoringElementContentWhitespace(false);
 8         factory.setCoalescing(false);
 9         factory.setExpandEntityReferences(true);
10         DocumentBuilder builder = factory.newDocumentBuilder();
11         builder.setEntityResolver(new XMLMapperEntityResolver());
      ...(此处省略一部分代码) 30 Document doc = builder.parse(Resources.getResourceAsStream("mybatis-config.xml")); 31 System.err.println(doc);

  在第11行中,用到了XMLMapperEntityResolver对象,这个对象定义如下:

 1 public class XMLMapperEntityResolver implements EntityResolver {
 2 
 3   private static final Map<String, String> doctypeMap = new HashMap<String, String>();
 4 
 5   private static final String IBATIS_CONFIG_PUBLIC = "-//ibatis.apache.org//DTD Config 3.0//EN".toUpperCase(Locale.ENGLISH);
 6   private static final String IBATIS_CONFIG_SYSTEM = "http://ibatis.apache.org/dtd/ibatis-3-config.dtd".toUpperCase(Locale.ENGLISH);
 7 
 8   private static final String IBATIS_MAPPER_PUBLIC = "-//ibatis.apache.org//DTD Mapper 3.0//EN".toUpperCase(Locale.ENGLISH);
 9   private static final String IBATIS_MAPPER_SYSTEM = "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd".toUpperCase(Locale.ENGLISH);
10 
11   private static final String MYBATIS_CONFIG_PUBLIC = "-//mybatis.org//DTD Config 3.0//EN".toUpperCase(Locale.ENGLISH);
12   private static final String MYBATIS_CONFIG_SYSTEM = "http://mybatis.org/dtd/mybatis-3-config.dtd".toUpperCase(Locale.ENGLISH);
13 
14   private static final String MYBATIS_MAPPER_PUBLIC = "-//mybatis.org//DTD Mapper 3.0//EN".toUpperCase(Locale.ENGLISH);
15   private static final String MYBATIS_MAPPER_SYSTEM = "http://mybatis.org/dtd/mybatis-3-mapper.dtd".toUpperCase(Locale.ENGLISH);
16 
17   private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
18   private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
19 
20   static {
21     doctypeMap.put(IBATIS_CONFIG_SYSTEM, MYBATIS_CONFIG_DTD);
22     doctypeMap.put(IBATIS_CONFIG_PUBLIC, MYBATIS_CONFIG_DTD);
23 
24     doctypeMap.put(IBATIS_MAPPER_SYSTEM, MYBATIS_MAPPER_DTD);
25     doctypeMap.put(IBATIS_MAPPER_PUBLIC, MYBATIS_MAPPER_DTD);
26 
27     doctypeMap.put(MYBATIS_CONFIG_SYSTEM, MYBATIS_CONFIG_DTD);
28     doctypeMap.put(MYBATIS_CONFIG_PUBLIC, MYBATIS_CONFIG_DTD);
29 
30     doctypeMap.put(MYBATIS_MAPPER_SYSTEM, MYBATIS_MAPPER_DTD);
31     doctypeMap.put(MYBATIS_MAPPER_PUBLIC, MYBATIS_MAPPER_DTD);
32   }
33 
34   /*
35    * Converts a public DTD into a local one
36    * 
37    * @param publicId The public id that is what comes after "PUBLIC"
38    * @param systemId The system id that is what comes after the public id.
39    * @return The InputSource for the DTD
40    * 
41    * @throws org.xml.sax.SAXException If anything goes wrong
42    */
43   @Override
44   public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
45 
46     if (publicId != null) {
47       publicId = publicId.toUpperCase(Locale.ENGLISH);
48     }
49     if (systemId != null) {
50       systemId = systemId.toUpperCase(Locale.ENGLISH);
51     }
52 
53     InputSource source = null;
54     try {
55       String path = doctypeMap.get(publicId);
56       source = getInputSource(path, source);
57       if (source == null) {
58         path = doctypeMap.get(systemId);
59         source = getInputSource(path, source);
60       }
61     } catch (Exception e) {
62       throw new SAXException(e.toString());
63     }
64     return source;
65   }
66 
67   private InputSource getInputSource(String path, InputSource source) {
68     if (path != null) {
69       InputStream in;
70       try {
71         in = Resources.getResourceAsStream(path);
72         source = new InputSource(in);
73       } catch (IOException e) {
74         // ignore, null is ok
75       }
76     }
77     return source;
78   }
79 
80 }

  可以看到,当框架在解析xml文件的时候,会把xml文件开头的publicId和systemId传给EntityResolver,而EntityResolver对象就是从classpath中去寻找.dtd文件(文件就在org/apache/ibatis/builder/xml/目录下),在spring中还会存在.xsd文件,原理都是一样的,然后利用classpath中的.dtd文件进行验证。如果不指定这个.dtd文件,那么会从互联网上面下载.dtd文件,性能不好。

mybatis,Spring等工具对xml文件正确性的验证