XMLConfigBuilder对XML文件的解析
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
parse.parse()是解析xml的开端。
/**
* 解析配置文件的入口方法
* @return Configuration对象
*/
public Configuration parse() {
// 不允许重复解析
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 从根节点开展解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration(parser.evalNode("/configuration"));
就是利用XPather中包含的jdk提供的XPath类对xml文件进行解析。
关于XPath的语法:
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse(Resources.getResourceAsFile("conf/mapperTest.xml"));
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
String name = (String) xPath.evaluate("/configuration/properties/property[1]/@name", doc, XPathConstants.STRING);
String value = (String) xPath.evaluate("/configuration/properties/property[1]/@value", doc, XPathConstants.STRING);
System.out.println(name);
System.out.println(value);
现在我们进入parser.evalNode("/configuration")去看看怎么执行的。
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);
}
/**
* 进行XML节点的解析
* @param expression 解析的语句
* @param root 解析根
* @param returnType 返回值类型
* @return 解析结果
*/
private Object evaluate(String expression, Object root, QName returnType) {
try {
// 对指定节点root运行解析语法expression,获得returnType类型的解析结果
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
}
}
我们在看看XNode具体是什么东西
public class XNode {
// org.w3c.dom.Node 表示是xml中的一个节点
private final Node node;
// 节点名
private final String name;
// 节点体
private final String body;
// 节点的属性
private final Properties attributes;
// MyBatis配置文件中的properties信息
private final Properties variables;
// XML解析器XPathParser
private final 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);
}
private Properties parseAttributes(Node n) {
Properties attributes = new Properties();
NamedNodeMap attributeNodes = n.getAttributes();
if (attributeNodes != null) {
for (int i = 0; i < attributeNodes.getLength(); i++) {
Node attribute = attributeNodes.item(i);
String value = PropertyParser.parse(attribute.getNodeValue(), variables);
attributes.put(attribute.getNodeName(), value);
}
}
return attributes;
}
很显然,这是一个mybatis自己写的增强版的Node,这个XNode的参数Node保存了该节点的信息,并用attributes保存节点属性名称和值的对应,用variables保存了Properties的信息。
<properties resource="conf/config.properties">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</properties>
- 以properties来说明,attributes保存节点属性名称和值的对,即保存了resource=conf/config.properties。
- 以property来说明,则保存了两个键值对,name=driver和value=${driver}