许多的Java框架都支持用户自己配置,其中很常见的就是使用XML文件进行配置。
本篇讲XML在Java中的解析,最后会简单地讲Mybatis在解析XML时的做法。
XML 文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///mybatis" />
<property name="username" value="root" />
<property name="password" value="121213" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mapper/IUserMapper.xml" />
</mappers>
</configuration>
XML 文件较为常见的就是上边的样子
- 第一行是文档头
- 第二行是文档类型定义(DTD,有时是Schema。作用都是为了保证文档正确)
- 其余的就是元素
需要注意的地方
- XML 是大小写敏感的
- XML 的属性必须用引号括起来
- XML 所有属性必须有值
Java DOM解析器
-
Java读入一个XML文件需要
DocumentBuilder
类,可以通过DocumentBuilderFactory
类构建。DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder();
-
之后就可以通过
DocumentBuilder
类的parse
方法读入一个XML文件啦。parse
接受多种参数,如File
、InputStream
等。InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml"); Document document = builder.parse(stream); 或 File file = new File("src/main/resources/mybatis-config.xml"); Document document = builder.parse(file);
-
此时就已经可以使用了,需要注意的一点就是,元素之间的空白字符也会被认为是子元素。
在没有使用DTD或Schema的情况下,需要我们手动判断元素是否继承自Element
。Node
就是我们XML文件上的一个元素,Node
类还有很多实用的方法,这里就不一一列举了。// 获取根元素 Element root = document.getDocumentElement(); // 获取孩子元素 NodeList childNodes = root.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); if (node instanceof Element) { System.out.println(node.getNodeName() + " " + node.getTextContent()); } }
-
如果使用了XML校验,也就是DTD或者Schema。在使用时可以进行设置。
解析器通过解析校验的文件,可以知道哪些元素没有文本节点的子元素,因此可以帮我们剔除空白字符。DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 开启校验 factory.setValidating(true); // 忽略空白字符 factory.setIgnoringElementContentWhitespace(true);
-
有个地方需要特殊处理,如果解析的是一个流的话,即
parse(inputStream)
,
并且在我们的XML文件中使用的是DTD文件的相对路径,
则需要提供一个实体解析器,用于指定DTD文件。<!-- XML中指定DTD文件时使用了相对位置 --> <!DOCTYPE configuration SYSTEM "mybatis-config.dtd"> // 实体解析器 public class MyEntityResolver implements EntityResolver { @Override public InputSource resolveEntity( String publicId, String systemId) throws SAXException, IOException { InputStream stream = Thread.currentThread() .getContextClassLoader() .getResourceAsStream("mybatis-config.dtd"); return new InputSource(stream); } } // 构建Builder时,设置实体解析器 DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(new MyEntityResolver());
Java XPath定位信息
在定位XML文件信息时,使用获取元素,再判断元素是否是目标元素的办法非常痛苦。
Java 为我们提供了好用的XPath
类。
-
创建
XPath
对象XPathFactory xPathFactory = XPathFactory.newInstance(); XPath xPath = xPathFactory.newXPath();
-
编写表达式,调用
evaluate
方法求值// Document document = ...; // 获取dataSource元素 String expression1 = "/configuration/environments/environment/dataSource"; Node node = (Node) xPath.evaluate( expression1, document, XPathConstants.NODE); // 也可以在当前已获得的节点下开始查找, 获取dateSource的type属性 String type = xPath.evaluate("@type", node); // 获取mappers下的第一个mapper子元素的resource属性,注意!索引是从1开始的 String expression2 = "/configuration/mappers/mapper[1]/@resource", document); String resource = xPath.evaluate( expression2, document);
Mybatis 解析XML
-
XPathParser
类
在mybatis中,解析XML使用了XPathParser
类,这个类是mybatis自定义的,
类中持有一个Document
对象,是我们的XML文件,还有一个XPath
对象。
类中提供了定位信息的方法,使用的就是Java提供的XPath
类。XPathParser
解析出的元素用一个XNode
对象存储。public class XPathParser { private Document document; private boolean validation; private EntityResolver entityResolver; private Properties variables; private XPath xpath; //... public XNode evalNode(String expression) { return evalNode(document, expression); } public XNode evalNode(Object root, String expression) { Node node = (Node) evaluate(expression, root, XPathConstants.NODE); if (node == null) { return null; } return new XNode(this, node, variables); } private Object evaluate(String expression, Object root, QName returnType) { try { return xpath.evaluate(expression, root, returnType); } catch (Exception e) { throw new BuilderException("Error evaluating XPath. Cause: " + e, e); } } //... }
-
XNode
类
mybatis将Node
类进一步封装,用XNode
表示。
当构造XNode
对象时,会自动解析出元素的元素名、元素的属性等。
此外XNode
中提供了获取子元素、获取父元素等行为,由于持有XPathParser
对象,XNode
中还提供了定位信息的方法。public class XNode { private Node node; private String name; private String body; private Properties attributes; private Properties variables; private XPathParser xpathParser; public XNode(XPathParser xpathParser, Node node, Properties variables) { this.xpathParser = xpathParser; this.node = node; this.name = node.getNodeName(); this.variables = variables; this.attributes = parseAttributes(node); this.body = parseBody(node); } public XNode evalNode(String expression) { return xpathParser.evalNode(node, expression); } ... }
- mybatis中,获取根元素只需这样写
XNode root = xPathParser.evalNode("/configuration");
之后获取configuration
元素下的mappers
是这样写root.evalNode("mappers")
- DTD
mybatis的XML文件使用了DTD,使用解析流的形式解析XML时,
mybatis也提供了实体解析器XMLMapperEntityResolver
,
mybatis的DTD文件路径是/org/apache/ibatis/builder/xml/mybatis-3-config.dtd
结语
了解Java提供的解析XML类,再去看各大框架如何解析XML就很容易了。
从这些框架中学习到如何封装好解析的行为,让我们使用的过程中,
不必花费太多功夫去获取XML文档信息,而是直接使用信息。这也是非常大的收获呀。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。