我们知道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文件,性能不好。
时间: 2024-10-26 08:36:39