MyBatis基础支持层位于 Mybatis 整体架构的最底层,支撑着 Mybatis 的核心处理层,是整个框架的基石。基础支持层中封装了多个较为通用的、独立的模块,不仅仅为 Mybatis 提供基础支撑,也可以在合适的场景中直接复用。
这篇文章介绍MyBatis的解析器模块
Mybatis
中涉及大量的XML
配置文件,常见的XML
解析方式:DOM、SAX和StAX。
XPath简介
MyBatis
在初始化过 程中处 理mybatis-config.xml
配置文件以及映射文件时 ,使用的是DOM
解析方式,并 结 合使用XPath
解析XML
配置文件。
正如前文所述,DOM
会将整个 XML
文档 加载 到内 存中并 形成树状数据结构 ,而 XPath
是一种 为 查 询 XML
文档 而设 计 的语 言,它 可以 与 DOM
解析方式配合使用,实 现 对 XML
文档 的解析。
Xpath 之于 XML 就好比 SQL 语言之于数据库。
XPathParser
Mybatis
提供的 Xpathparser
类封装了Xpath
、Document
和Entityresolver
。
Xpathparser
中各个字段的含义和功能如下所示。
private Document document; // Document 对象 private boolean validation; //是否开启验证
private Entityresolver entityresolver; //用于加载本地 DTD 文件
private Properties variables; // mybatis- config. Xm1 中《propteries》标签定义的键值对集合
private Xpath xpath; // Xpath 对象
默认情况下,对 XML
文档进行验证时,会根据 XML
文档开始位置指定的网址加载对应的 DTD
文件或 XSD
文件。
如果解析 mybatis- config. Xml
配置文件,默认联网加载 http: / mybatis. Org, / dtd/mybatis-3- config. Dtd
这个 DTD
文档,当网络比较慢时会导致验证过程缓慢。
在实践中往往会提前设置 Entityresolver
接口对象加载本地的 DTD
文件,从而避免联网加载 DTD
文件。
Xmlmapperentity Resolver
是 Mybatis 提供的 Entity Resolver
接口的实现类。
EntityResolver
接口的核心是 resolveEntity()
方法,XMLMapperEntityResolver
的实 现 如下
public class XMLMapperEntityResolver implements EntityResolver {
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";
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
try {
if (systemId != null) {
String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH);
if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM)
|| lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) {
return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId);
} else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM)
|| lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) {
return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId);
}
}
return null;
} catch (Exception e) {
throw new SAXException(e.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 e) {
// ignore, null is ok
}
}
return source;
}
}
回到对 XPathParser
的 分 析 ,在 XPathParser. createDocument()
方法中封装 了前面介绍 的创 建Document
对 象的过 程并 触 发 了加载 XML
文档 的 过 程,具体 实 现 如下:
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();
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
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 {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
Xpathparser
中提供了一系列的eval*0
方法用于解析boolean
、shot
、long
、int
、String
、Node
等类型的信息,它通过调用前面介绍的Xpath. Evaluate()
方法查找指定路径的节点或属性,并进行相应的类型装换。具体代码比较简单,就不贴出来了。这里需要注意的是Xpathparser. Evalstring()
方法,其中会调用Propertyparser. Parse()
方法处理节点中相应的默认值。在
Propertyparser
中指定了是否开启使用默认值的功能以及默认的分隔符PropertyParser.parse()
方法中会创建 Generic Tokenparser 解析器,并将默认值的处理委托给GenericTokenparser.parse()
方法。Generic Tokenparser 是一个通用的字占位符解析器,其字段的含义如下:
Private final String opentoken; //占位符的开始标记 private final String closetoken; //占位符的结東标记 private final Tokenhandler handler; // Tokenhandler 接口的实现会按照一定的逻辑解析占位符
GenericTokenparser.parse()
方法的逻辑并不复杂,它会顺序查找openToken
和closeToken
,解析得到占位符的字面值,并将其交给Tokenhandler
处理,然后将解析结果重新拼装成字符串并返回。
参考
《MyBatis技术内幕》
部分图片来源——《MyBatis技术内幕》

相关推荐
版权声明
- 本文链接: https://www.cayzlh.com/2019/10/24/94150c5.html
- 版权声明: 文章内容仅用作学习和分享,如有雷同,纯属拷贝。
留言区