許多的Java框架都支持用戶本身配置,其中很常見的就是使用XML文件進行配置。
本篇講XML在Java中的解析,最後會簡單地講Mybatis在解析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 文件較爲常見的就是上邊的樣子node
須要注意的地方mysql
Java讀入一個XML文件須要DocumentBuilder
類,能夠經過DocumentBuilderFactory
類構建。sql
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder();
以後就能夠經過DocumentBuilder
類的parse
方法讀入一個XML文件啦。parse
接受多種參數,如File
、InputStream
等。express
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
類還有不少實用的方法,這裏就不一一列舉了。apache
// 獲取根元素 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。在使用時能夠進行設置。
解析器經過解析校驗的文件,能夠知道哪些元素沒有文本節點的子元素,所以能夠幫咱們剔除空白字符。mybatis
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 開啓校驗 factory.setValidating(true); // 忽略空白字符 factory.setIgnoringElementContentWhitespace(true);
有個地方須要特殊處理,若是解析的是一個流的話,即parse(inputStream)
,
而且在咱們的XML文件中使用的是DTD文件的相對路徑,
則須要提供一個實體解析器,用於指定DTD文件。app
<!-- 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());
在定位XML文件信息時,使用獲取元素,再判斷元素是不是目標元素的辦法很是痛苦。
Java 爲咱們提供了好用的XPath
類。框架
建立XPath
對象ide
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);
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); } ... }
XNode root = xPathParser.evalNode("/configuration");
configuration
元素下的mappers
是這樣寫root.evalNode("mappers")
XMLMapperEntityResolver
,/org/apache/ibatis/builder/xml/mybatis-3-config.dtd
瞭解Java提供的解析XML類,再去看各大框架如何解析XML就很容易了。從這些框架中學習到如何封裝好解析的行爲,讓咱們使用的過程當中,沒必要花費太多功夫去獲取XML文檔信息,而是直接使用信息。這也是很是大的收穫呀。