一.接口和类介绍
1.TokenHandler
public interface TokenHandler {
String handleToken(String content);
}
这个接口中只有一个函数,就是对字符串进行处理。将db.properties中参数中占位符${xxx}进行参数替换
。
实现类
private static class VariableTokenHandler implements TokenHandler
解析形如:“username:default” 字符串 ,先获取key(username),然后从配置集合中查询对应的value,并返回value的值。
如果集合variables 集合不存在,返回 ${content}
private static class VariableTokenHandler implements TokenHandler {
private final Properties variables;
private final boolean enableDefaultValue;
private final String defaultValueSeparator;
//构造函数初始化 handle
private VariableTokenHandler(Properties variables) {
this.variables = variables;
//字符串转化为false/true,初始化值为“true”或者“false” 是字符串
this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
//获取分隔符号,DEFAULT_VALUE_SEPARATOR 在外部类中定义了
this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
}
private String getPropertyValue(String key, String defaultValue) {
return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
}
@Override
// 解析key,根据key返回 variables 中的 value
public String handleToken(String content) {
//content=db.username:postgres
//1.按照分割符分割指定的占位符号,得到占位符号的名称和默认值
//然后按照分割得到的占位符号名称找到对应的值
// 如果在properties 中未定义对应的键值对,则切分的得到的默认值作为解析结果返回
if (variables != null) {//检测集合是否为空
//isEmpty() 用于判断List内容是否为空,即表里一个元素也没有,
//list等于null,可理解为没有对list集合分配内存空间,实际上压根就不存在
//System.out.println("if (variables != null)");
String key = null;// 意义何在?
if (enableDefaultValue) {//判断是否开启了占位符中使用默认值
final int separatorIndex = content.indexOf(defaultValueSeparator);
//查找分隔符号
String defaultValue = null;
if (separatorIndex >= 0) {
//key
key = content.substring(0, separatorIndex);//key
//默认值value
defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
}
if (defaultValue != null) {
//查找指定的key 的value是否存在,不存在则为默认值
return variables.getProperty(key, defaultValue);
}
}
//如果在集合中存在key,返回对应的值k
if (variables.containsKey(key)) {
return variables.getProperty(key);
}
}
//如果参数集合为null,则返
//我觉得,这样做没什么实际意
return "${" + content + "}";
}
}
2.GenericTokenParser 一般意义上的字符串解析
字符串处理:形如“www.${name:default}
.sankuai.com” 提取出”name:default” 并用xule05 代替 ${name:default}
最终返回的字符串为:www.xule05.sankuai.com
package org.apache.ibatis.parsing;
/**
* @author Clinton Begin
*/
public class GenericTokenParser {
private final String openToken;//占位符开始 ${
private final String closeToken;//占位符结束 }
private final TokenHandler handler;// 解析占位符 key:value
......
//GenericTokenParser中唯一方法
//例如解析www.${name:default}.sankuai.com
public String parse(String text) {//获取节点的文本属性
//1.将” ${ “之前的字符串添加到builder中
......
//2.将解析到的字符串添加到builder,参数填充委托给handle处理
builder.append(handler.handleToken(expression.toString()));
//3. 将“}”之后的字符串添加到builder
......
return builder.toString();
}
简单的说,这个函数的作用就是将openToken和endToken间的字符串取出来用handler处理下,去掉”${” 和”}“然后再拼接到一块。我们接下来看一个具体的handler,了解下它对传入的字符串做了怎样的处理。
package org.apache.ibatis.parsing;
/**
* @author Clinton Begin
*/
public class GenericTokenParser {
private final String openToken;//占位符开始${
private final String closeToken;//占位符结束}
private final TokenHandler handler;// 占位符处理器:解析占位符 key:value
//构造函数初始化上述三个字段
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
//按照顺序查找openToken closeToken 解析得到占位符号,交给handle处理
//将解析结果重新拼装为字符串返回
//text === ${key:defaultvalue}
//我觉得这个方法考虑的太复杂了
public String parse(String text) {
if (text == null || text.isEmpty()) {
return "";
}
// search open token
int start = text.indexOf(openToken, 0);//从指定位置查找出现的字符串 等同于 indexOf(openToken)
if (start == -1) {
return text;
}
char[] src = text.toCharArray();
int offset = 0;//定义的偏移量很经典,值得学习!!!用一个偏移量解决了
// 使用final 的原因是???
final StringBuilder builder = new StringBuilder();//用来记录解析后的字符串
StringBuilder expression = null;//用来记录一个占位符的字符串
while (start > -1) {//为什么要用while 循环
if (start > 0 && src[start - 1] == '\\') {
// this open token is escaped. remove the backslash and continue.
// 可能字符串开始为 \\$ 先删除反斜线,继续解析字符串
//这个开放的标记被转义。 删除反斜杠并继续
//遇到转义的字符串,直接将前面的字符串以及开始标记追加到builder中
// "builderpath\\${url:www.baidu.com\\}"
builder.append(src, offset, start - offset - 1).append(openToken);
//builderpath${
offset = start + openToken.length();// offset 指向url中第一个字母
} else {
//查找到开始标记,而且未被转义
// found open token. let's search close token.
// 开始标记已经找到,寻找结束标记
if (expression == null) {//为什么不直接赋值???
expression = new StringBuilder();
} else {
expression.setLength(0);//设置为0 有事什么意识???
}
//将前面的字符串追加到builder中
builder.append(src, offset, start - offset);
offset = start + openToken.length();
//查找结束标记
int end = text.indexOf(closeToken, offset);
while (end > -1) {
if (end > offset && src[end - 1] == '\\') {
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
if (end == -1) {
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
//调用处理器处理字符串函数
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
//System.out.println(builder.toString());
return builder.toString();
}
3.PropertyParser 从类的名称上看是 参数(Property)解析(Parser)
//核心方法
public static String parse(String string, Properties variables) {
//先初始化一个handler
VariableTokenHandler handler = new VariableTokenHandler(variables);
//在初始化GenericTokenParser对象,设置openToken为${,endToken为}
//有没有对${}比较熟悉,这个符号就是mybatis配置文件中的占位符,例如定义datasource时用到的 <property name="driverClassName" value="${driver}" />
//同时也可以解释在VariableTokenHandler中的handleToken时,如果content在properties中不存在时,返回的内容要加上${}了。
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
//返回一个不带${key}且ke被正确解析的字符串
return parser.parse(string);
}
public class PropertyParser {
private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
//指示是否在占位符上启用默认值的特殊属性键。默认值为false,表示在占位符上禁用默认值
//如果指定了true,则可以在占位符上指定键和默认值(例如$ {db.username:postgres}
public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";
private static final String ENABLE_DEFAULT_VALUE = "true";
//为占位符指定键和默认值的分隔符的特殊属性键。
public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";
// 分隔符号为 :
private static final String DEFAULT_VALUE_SEPARATOR = ":";
//================
// 第一个键值对:
//指示是否在占位符上启用默认值的特殊属性键。默认值为false,表示在占位符上禁用默认值
//如果指定了true,则可以在占位符上指定键和默认值(例如$ {db.username:postgres}
// key=org.apache.ibatis.parsing.PropertyParser.enable-default-value
// value=false(默认)禁用默认值
//第二个键值对:
//为占位符指定键和默认值的分隔符的特殊属性键。
//key=org.apache.ibatis.parsing.PropertyParser.default-value-separator
//value=: (默认值)
private PropertyParser() {
// Prevent Instantiation
}
//核心方法
public static String parse(String string, Properties variables) {
//生产一个handle
VariableTokenHandler handler = new VariableTokenHandler(variables);
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
return parser.parse(string);
}
//解析并返回默认值,持有variableTokenHandler参数集合的引用
private static class VariableTokenHandler implements TokenHandler {
private final Properties variables;
private final boolean enableDefaultValue;
private final String defaultValueSeparator;
//构造函数初始化 handle
private VariableTokenHandler(Properties variables) {
this.variables = variables;
//字符串转化为false/true
this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
//获取分隔符号
this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
}
private String getPropertyValue(String key, String defaultValue) {
return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
}
//返回key的在variables中值,variables==null ,返回${key}
@Override
public String handleToken(String content) {
//content=db.username:postgres
//1.按照分割符分割指定的占位符号,得到占位符号的名称和默认值
//然后按照分割得到的占位符号名称找到对应的值
// 如果在properties 中未定义对应的键值对,则切分的得到的默认值作为解析结果返回
if (variables != null) {//检测集合是否为空
//isEmpty() 用于判断List内容是否为空,即表里一个元素也没有,
//list等于null,可理解为没有对list集合分配内存空间,实际上压根就不存在
//System.out.println("if (variables != null)");
String key = null;// 意义何在?
if (enableDefaultValue) {//判断是否开启了占位符中使用默认值
final int separatorIndex = content.indexOf(defaultValueSeparator);
//查找分隔符号
String defaultValue = null;
if (separatorIndex >= 0) {
//key
key = content.substring(0, separatorIndex);//key
//默认值value
defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
}
if (defaultValue != null) {
//查找指定的key 的value是否存在,不存在则为默认值
return variables.getProperty(key, defaultValue);
}
}
//如果在集合中存在key,返回对应的值k
if (variables.containsKey(key)) {
return variables.getProperty(key);
}
}
//如果参数集合为空,则返
//System.out.println("${" +1243+ "}");
return "${" + content + "}";
}
}
public static void main(String[] args){
Properties variables =new Properties();
variables.put("key","2016");
variables.put("密码","123gsycgjabc");
VariableTokenHandler handler = new VariableTokenHandler(variables);
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
String s = handler.handleToken("key:value");
System.out.println(s);
String parse = parser.parse("xule${密码:1234455555}");
System.out.println(parse);
}
}
输出结果差异:
handle.handleToken
GenericTokenParser.parser
2016
xule123gsycgjabc
4.XPathParser
属性
private final Document document;//Document对象
private boolean validation;//是否开启验证
private EntityResolver entityResolver;//加载本地DTD文件
private Properties variables;//mybatis-config.xml 中的 properties 标签下的键值对集合
private XPath xpath;//XML 查询对象
打开这个类的outline会发现这个类包含的函数真的是“蔚为壮观”,虽然数量众多,基本上可以分为两类:初始化(构造函数)、evalXXX。
4.1初始化:
构造多达10几个
XPathParser类的构造函数数量众多,是由于这个类的属性比较多,这些构造函数内部都会调用到如下函数:commonConstructor和createDocument。接下来我们来看看这两个函数具体做了那些事情:
//注意:这是一个非构造方法:为什么不使用构造器,会被其他构造器调用
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
//利用XPathFactory创建一个新的xpath对象
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
// 看这个commo怎么被调用的
public XPathParser(String xml) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
加mybatis-config.xml并创建文档Document
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
// 重要:这只能在公共构造函数后调用
try {
//1.文档创建者工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
//2.文档创建者
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
//3.创建doc文档
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
4.2 evalXXX
这个类中的evalXXX函数有两种多态形式:一种是只有一个expression参数;另一种则有两个函数,除了expression参数外还包含一个root参数。像我们经常见到的那样,带一个参数的evalXXX函数会在设置一个默认值后调用带有两个参数的函数,我们看一个具体的例子evalString:
public String evalString(String expression) {
return evalString(document, expression);
}
public String evalString(Object root, String expression) {
String result = (String) evaluate(expression, root, XPathConstants.STRING);
result = PropertyParser.parse(result, variables);
return result;
}
//expression表达式XPath表达式。
//root起始上下文(例如一个节点)。
//returnType所需的返回类型。
private Object evaluate(String expression, Object root, QName returnType) {
try {
//在指定的上下文中解析一个XPath表达式,并返回结果作为指定的类型
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
}
}
package org.apache.ibatis.parsing;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.ibatis.builder.BuilderException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* @author Clinton Begin
*/
//Mybatis 提供的XPathParser 类封装了XPath Document EntityResolver
//XPathParser作用加载本地文档并解析文档的属性到Properties中
public class XPathParser {
//创建过程复杂,可不可以考虑builder模式
private final Document document;//Document对象
private boolean validation;//是否开启验证
private EntityResolver entityResolver;//加载本地DTD文件
private Properties variables;//mybatis-config.xml 中的 properties 标签下的键值对集合
private XPath xpath;//XML 查询对象
public XPathParser(String xml) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document) {
commonConstructor(false, null, null);
this.document = document;
}
public XPathParser(String xml, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation) {
commonConstructor(validation, null, null);
this.document = document;
}
public XPathParser(String xml, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = document;
}
public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = document;
}
public void setVariables(Properties variables) {
this.variables = variables;
}
public String evalString(String expression) {
return evalString(document, expression);
}
public String evalString(Object root, String expression) {
String result = (String) evaluate(expression, root, XPathConstants.STRING);
result = PropertyParser.parse(result, variables);
return result;
}
public Boolean evalBoolean(String expression) {
return evalBoolean(document, expression);
}
public Boolean evalBoolean(Object root, String expression) {
return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN);
}
public Short evalShort(String expression) {
return evalShort(document, expression);
}
public Short evalShort(Object root, String expression) {
return Short.valueOf(evalString(root, expression));
}
public Integer evalInteger(String expression) {
return evalInteger(document, expression);
}
public Integer evalInteger(Object root, String expression) {
return Integer.valueOf(evalString(root, expression));
}
public Long evalLong(String expression) {
return evalLong(document, expression);
}
public Long evalLong(Object root, String expression) {
return Long.valueOf(evalString(root, expression));
}
public Float evalFloat(String expression) {
return evalFloat(document, expression);
}
public Float evalFloat(Object root, String expression) {
return Float.valueOf(evalString(root, expression));
}
public Double evalDouble(String expression) {
return evalDouble(document, expression);
}
public Double evalDouble(Object root, String expression) {
return (Double) evaluate(expression, root, XPathConstants.NUMBER);
}
public List<XNode> evalNodes(String expression) {
return evalNodes(document, expression);
}
public List<XNode> evalNodes(Object root, String expression) {
List<XNode> xnodes = new ArrayList<XNode>();
NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
xnodes.add(new XNode(this, nodes.item(i), variables));
}
return xnodes;
}
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);
}
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
// 重要:这只能在公共构造函数后调用
try {
//1.文档创建者工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
//2.文档创建者
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
//3.创建doc文档
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
//为什么使用构造器?????,只是使用了一个很简单的方法
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
//利用XPathFactory创建一个新的xpath对象
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
}
5.XNode
接下来我们来了解parsing包中的最后一个类XNode。该类是对org.w3c.dom.Node类的一个封装,在Node类的基础上添加了一些新功能。
private final Node node;//org.w3c.dom.Node对象
private final String name;//Node节点名称
private final String body;//节点的内容????
private final Properties attributes;//节点属性集合
private final Properties variables;//<property> </property> 节点下定义的键值对
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);//获取当前节点的所有属性
//获取当前节点的文本节点内容,当然获取到的数据是已经经过TokenHandler处理过的
this.body = parseBody(node);
}
//获取当前节点的所有属性
//属性节点 <span color="red" size="16" font="12">text</span>
// color,size,font 属性
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;
}
// 解析文本节点的内容
private String parseBody(Node node) {
String data = getBodyData(node);
if (data == null) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
data = getBodyData(child);
if (data != null) {
break;
}
}
}
return data;
}
private String getBodyData(Node child) {
if (child.getNodeType() == Node.CDATA_SECTION_NODE
|| child.getNodeType() == Node.TEXT_NODE) {
//判断子节点是否为文本节点或者文本节点的子节点
String data = ((CharacterData) child).getData();
data = PropertyParser.parse(data, variables);
return data;
}
return null;
}
package org.apache.ibatis.parsing;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author Clinton Begin
*/
public class XNode {
private final Node node;//org.w3c.dom.Node对象
private final String name;//Node节点名称
private final String body;//节点的内容????
private final Properties attributes;//节点属性集合
private final Properties variables;//<property> </property> 节点下定义的键值对
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);
}
//将传入的Node节点包装为XNode类型
public XNode newXNode(Node node) {
return new XNode(xpathParser, node, variables);
}
//获取Node节点的父节点
public XNode getParent() {
//调用node节点原生方法获取一个Node类型的节点
Node parent = node.getParentNode();
if (parent == null || !(parent instanceof Element)) {
//父节点为null或者节点的类型为不为元素节点
return null;
} else {
//获取XNode的父亲(双亲),还是使用原始的node的操作,之后转化成XNode
return new XNode(xpathParser, parent, variables);
}
}
//获取Node在XML中的完整路径
public String getPath() {
StringBuilder builder = new StringBuilder();
Node current = node;
//递归调用,不为空且为元素节点
while (current != null && current instanceof Element) {
if (current != node) {
builder.insert(0, "/");
}
builder.insert(0, current.getNodeName());
current = current.getParentNode();
}
return builder.toString();
}
//?????不知道干什么用
public String getValueBasedIdentifier() {
StringBuilder builder = new StringBuilder();
XNode current = this;
while (current != null) {
if (current != this) {
builder.insert(0, "_");
}
String value = current.getStringAttribute("id",
current.getStringAttribute("value",
current.getStringAttribute("property", null)));
if (value != null) {
// 注意StringBuilder使用的插入模式,好像这个是的动态的字符串,,可以随便的插入和删除操作,很方便
value = value.replace('.', '_');
builder.insert(0, "]");
builder.insert(0,
value);
builder.insert(0, "[");
}
builder.insert(0, current.getName());
current = current.getParent();
}
return builder.toString();
}
/**
* 下面是引用XPathParse的一些解析方法
*
* @param expression 解析的表达式
* @return 解析之后的值
*/
public String evalString(String expression) {
return xpathParser.evalString(node, expression);
}
public Boolean evalBoolean(String expression) {
return xpathParser.evalBoolean(node, expression);
}
public Double evalDouble(String expression) {
return xpathParser.evalDouble(node, expression);
}
public List<XNode> evalNodes(String expression) {
return xpathParser.evalNodes(node, expression);
}
public XNode evalNode(String expression) {
return xpathParser.evalNode(node, expression);
}
public Node getNode() {
return node;
}
public String getName() {
return name;
}
/**
* 下面都是应用getStringAttribute的方法获取的值
*/
public String getStringBody() {
return getStringBody(null);
}
public String getStringBody(String def) {
if (body == null) {
return def;
} else {
return body;
}
}
public Boolean getBooleanBody() {
return getBooleanBody(null);
}
public Boolean getBooleanBody(Boolean def) {
if (body == null) {
return def;
} else {
return Boolean.valueOf(body);
}
}
public Integer getIntBody() {
return getIntBody(null);
}
public Integer getIntBody(Integer def) {
if (body == null) {
return def;
} else {
return Integer.parseInt(body);
}
}
public Long getLongBody() {
return getLongBody(null);
}
public Long getLongBody(Long def) {
if (body == null) {
return def;
} else {
return Long.parseLong(body);
}
}
public Double getDoubleBody() {
return getDoubleBody(null);
}
public Double getDoubleBody(Double def) {
if (body == null) {
return def;
} else {
return Double.parseDouble(body);
}
}
public Float getFloatBody() {
return getFloatBody(null);
}
public Float getFloatBody(Float def) {
if (body == null) {
return def;
} else {
return Float.parseFloat(body);
}
}
public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name) {
return getEnumAttribute(enumType, name, null);
}
public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name, T def) {
String value = getStringAttribute(name);
if (value == null) {
return def;
} else {
return Enum.valueOf(enumType, value);
}
}
public String getStringAttribute(String name) {
return getStringAttribute(name, null);
}
public String getStringAttribute(String name, String def) {
String value = attributes.getProperty(name);
if (value == null) {
return def;
} else {
return value;
}
}
public Boolean getBooleanAttribute(String name) {
return getBooleanAttribute(name, null);
}
public Boolean getBooleanAttribute(String name, Boolean def) {
String value = attributes.getProperty(name);
if (value == null) {
return def;
} else {
return Boolean.valueOf(value);
}
}
public Integer getIntAttribute(String name) {
return getIntAttribute(name, null);
}
public Integer getIntAttribute(String name, Integer def) {
String value = attributes.getProperty(name);
if (value == null) {
return def;
} else {
return Integer.parseInt(value);
}
}
public Long getLongAttribute(String name) {
return getLongAttribute(name, null);
}
public Long getLongAttribute(String name, Long def) {
String value = attributes.getProperty(name);
if (value == null) {
return def;
} else {
return Long.parseLong(value);
}
}
public Double getDoubleAttribute(String name) {
return getDoubleAttribute(name, null);
}
public Double getDoubleAttribute(String name, Double def) {
String value = attributes.getProperty(name);
if (value == null) {
return def;
} else {
return Double.parseDouble(value);
}
}
public Float getFloatAttribute(String name) {
return getFloatAttribute(name, null);
}
public Float getFloatAttribute(String name, Float def) {
String value = attributes.getProperty(name);
if (value == null) {
return def;
} else {
return Float.parseFloat(value);
}
}
//获取孩子节点的List<XNode>集合
public List<XNode> getChildren() {
List<XNode> children = new ArrayList<XNode>();
NodeList nodeList = node.getChildNodes();
if (nodeList != null) {
for (int i = 0, n = nodeList.getLength(); i < n; i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
//包装为XNode 节点
children.add(new XNode(xpathParser, node, variables));
}
}
}
return children;
}
//获取当前节点的所有孩子节点的属性值name/value
public Properties getChildrenAsProperties() {
Properties properties = new Properties();
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("<");
builder.append(name);
for (Map.Entry<Object, Object> entry : attributes.entrySet()) {
builder.append(" ");
builder.append(entry.getKey());
builder.append("=\"");
builder.append(entry.getValue());
builder.append("\"");
}
List<XNode> children = getChildren();
if (!children.isEmpty()) {
builder.append(">\n");
for (XNode node : children) {
builder.append(node.toString());
}
builder.append("</");
builder.append(name);
builder.append(">");
} else if (body != null) {
builder.append(">");
builder.append(body);
builder.append("</");
builder.append(name);
builder.append(">");
} else {
builder.append("/>");
}
builder.append("\n");
return builder.toString();
}
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;
}
private String parseBody(Node node) {
String data = getBodyData(node);
if (data == null) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
data = getBodyData(child);
if (data != null) {
break;
}
}
}
return data;
}
private String getBodyData(Node child) {
if (child.getNodeType() == Node.CDATA_SECTION_NODE
|| child.getNodeType() == Node.TEXT_NODE) {
//判断子节点是否为文本节点或者文本节点的子节点
String data = ((CharacterData) child).getData();
data = PropertyParser.parse(data, variables);
return data;
}
return null;
}
}
二.最佳实战
我们写一个示例看看这个包中的类是如何运行的。从上面的类和接口的介绍中可以发现,XPathParser类是比较上层的类
(这里的上层,不是说这个类是各个类的超类,而是说它依赖的类较多)。用XPathParser类解析一段数据源定义的配置文件片段,首先设定properties文件的内容,文件名为jdbc.properties:
package org.apache.ibatis.parsing;
import org.apache.ibatis.io.Resources;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class Main {
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties properties = new Properties();
//加载参数: properties.load();
properties.load(new FileInputStream("src/main/java/org/apache/ibatis/parsing/config/config.xml"));
String xml ="<?xml version='1.0' encoding='utf-8'?>"+
"<bean id='dataSource' class='org.apache.commons.dbcp.BasicDataSource' destroy-method='close' > " +
" <property name='driverClassName' value='${driver}' />" +
" <property name='url' value='${url}' /> " +
" <property name='username' value='${username}' /> " +
" <property name='password' value='${password}' /> " +
"</bean>";
//初始化XPathParser
XPathParser xPathParser = new XPathParser(xml,false,properties);
//解析表达式,获取XNode对象
XNode xnode = xPathParser.evalNode("//bean");
//下面调用对
System.out.println(xnode.getName());
System.out.println(xnode.getStringAttribute("id"));
System.out.println(xnode.getStringAttribute("class"));
System.out.println(xnode.getPath());
}
}
jdbc.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=1q2w3e
输出结果
bean
dataSource
org.apache.commons.dbcp.BasicDataSource
bean