Mybatis基础支持层-解析器模块
Mybatis三层架构:接口层,核心处理层,基础支持层 基础支持层:数据源模块、反射模块、缓存模块、日志模块、事务管理模块、Binding模块、类型转换、资源解析、解析器模块
一 XML内容解析
-
之前有一篇博文,详细描述了多种XML解析方式:Dom SAX StAX
-
XPath: 使用路径表达式来选取XML文档中指定的节点或者节点的集合
nodename 选取指定节点的所有子节点
/ 从根节点选取指定节点
// 根据指定表达式,在整个文档中选取匹配的节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
* 匹配任何元素节点
@* 匹配任何属性节点
node() 匹配任何类型的节点
text() 匹配文本节点
| 选取若干个路径
[] 指定某个条件
- 使用示例
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse("store.xml");
List<Product> products = new ArrayList<>();
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
XPathExpression expression = xPath.compile("//product[name='薯片']/price/text()");
Double price = (Double)expression.evaluate(document,XPathConstants.NUMBER);
System.out.println(price);
NodeList nodeList = (NodeList) xPath.evaluate("//product[@id>2]/name/text()", document, XPathConstants
.NODESET);
for(int i =0 ;i<nodeList.getLength() ;i++){
System.out.println(nodeList.item(i).getNodeValue());
}
二 XPathParser
- 封装了XPath / Document / EntityResolver
public class XPathParser {
/**
* Document对象
*/
private final Document document;
/**
* 是否开启验证
*/
private boolean validation;
/**
* 用于加载本地DTD文件
*/
private EntityResolver entityResolver;
/**
* mybatis-config.xml中>properties> 标签定义的键值对集合
*/
private Properties variables;
/**
* XPath 对象
*/
private XPath xpath;
...
}
- 为了避免远程加载DTD文件,EntityResolver会从本地加载一份, Mybatsi中有XMLMapperEntityResolver的接口实现
public class XMLMapperEntityResolver implements EntityResolver {
//mybatis-config.xml文件和映射文件对应的DTD的systemId
private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";
private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
public XMLMapperEntityResolver() {
}
//核心实现的方法
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
try {
if (systemId != null) {
//根据SystemId获取DTD文件
String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
if (lowerCaseSystemId.contains("mybatis-3-config.dtd") || lowerCaseSystemId.contains("ibatis-3-config.dtd")) {
return this.getInputSource("org/apache/ibatis/builder/xml/mybatis-3-config.dtd", publicId, systemId);
}
if (lowerCaseSystemId.contains("mybatis-3-mapper.dtd") || lowerCaseSystemId.contains("ibatis-3-mapper.dtd")) {
return this.getInputSource("org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd", publicId, systemId);
}
}
return null;
} catch (Exception var4) {
throw new SAXException(var4.toString());
}
}
private InputSource getInputSource(String path, String publicId, String systemId) {
InputSource source = null;
if (path != null) {
try {
InputStream in = Resources.getResourceAsStream(path);
source = new InputSource(in);
source.setPublicId(publicId);
source.setSystemId(systemId);
} catch (IOException var6) {
}
}
return source;
}
}
- 正式加载XML文档,XPathParser的createDocument方法,代码是之前比较常见的XML文件的解析步骤
private Document createDocument(InputSource inputSource) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(this.validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(this.entityResolver);
builder.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception var4) {
throw new BuilderException("Error creating document instance. Cause: " + var4, var4);
}
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
- XPathParser.evalString() 会处理节点中的默认值
public String evalString(Object root, String expression) {
String result = (String)this.evaluate(expression, root, XPathConstants.STRING);
//设置默认值
result = PropertyParser.parse(result, this.variables);
return result;
}
-
PropertyParser中 static final String KEY_ENABLE_DEFAULT_VALUE = “org.apache.ibatis.parsing.PropertyParser.enable-default-value”; 对应mybatis-config.xml文件中< properties > 中是否配置了开启默认值功能
-
PropertyParser.parse最终将解析的任务交给GenericTokenParser parser.parse实现
public static String parse(String string, Properties variables) {
PropertyParser.VariableTokenHandler handler = new PropertyParser.VariableTokenHandler(variables);
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
return parser.parse(string);
}
- GenericTokenParser 是一个通用字符占位符解析器,基本就是字符串匹配,有开始占位符,结束占位符,对应如何解析,逐个查找开始和结束符号,最终将参数拿到
public class GenericTokenParser {
private final String openToken;
private final String closeToken;
private final TokenHandler handler;
...
}
- TokenHandler有四个实现类(VariableTokenHandler,ParameterMappingTokenHandler,DynamicCheckerTokenParser,BindingTokenParser),分别用于解析输入参数、动态检查、参数绑定、变量,对于开启检测的变量解析,会使用到VariableTokenHandler,DynamicCheckerTokenParser
private static class VariableTokenHandler implements TokenHandler {
private final Properties variables;
private final boolean enableDefaultValue;
private final String defaultValueSeparator;
...
public String handleToken(String content) {
//按照默认的分隔符,进行字符串查找,分割出占位符名称,塞入Properties中,如果开启默认值形式,并且解析未果,则会填入默认值
}
}
-
因而 如果是参数${usernamae:root},首先按照 : 分割,获取username字段,接着在variables里面查找值,如果不存在填入root的值,放入properties中
-
前面XPathParser 还有evalNode ,获取一个XNode 是对Node的封装,会绑定Properties 于XPathParser对象。 XNode中的parseAttributes parseBody都是用了getAttributes 于getChildNodes的熟悉方法实现的,XNode 的eval()方法,也是和XPath的eval()方法是类似的