一、MyBatis框架图及瞎BB
先看图:
(图片来源:《MyBatis技术内幕》)
Mybatis加载mybatis-config.xml的过程其实就是 MyBatis的基础支持层的解析器模块的工作流程,此篇博客以加载一个xml文件为例来让你对解析器的有个全面的了解。本人是一步一步跟着源码调试走下去的,不敢说自己很了解,只是把自己的试验结果分享出来,当做笔记同时也能帮助别人。(妈的,说话间头发又掉了几根在桌子上,程序挺好搞的,就是头有点凉。)
二、Xml配置文件的解析方式
常见xml的解析方式有三种分别是:DOM ( Document Object Model )解析方式和SAX ( Simple API for XML )解析方式,以及从JDK6.0 版本开始, JDK 开始支持的StAX ( Streaming API for XML)解析方式。在开始介绍My Batis 的XML 解析功能之前,先介绍这几种常见的XML 处理方式。
1.Dom解析方式:Dom解析方式是将整个文档加载到整个内存中形成树结构,我们很容易根据树形结构找到兄弟、儿子、子子孙孙等节点。这样我们就能很快找到我们想要的数据了,但是这种方式需要加载整个文档所以会非常的耗费内存资源。
2.SAX解析方式:SAX 是基于事件模型的XML 解析方式,它并不需要将整个XML 文档加载到内存中,而只需将XML 文档的一部分加载到内存中,即可开始解析,在处理过程中井不会在内存中记录XML 中的数据,所以占用的资源比较小。当程序处理过程中满足条件时,也可以立即停止解析过程,这样就不必解析剩余的XML 内容。当SAX 解析器解析到某类型节点时,会触发注册在该类型节点上的回调函数,开发人员可以根据自己感兴趣的事件注册相应的回调函数。一般情况下,开发人员只需继承SAX 提供的DefaultHandler 基类,重写相应事件的处理方法并进行注册即可。SAX 的缺点也非常明显,因为不存储XML 文挡的结构,所以需要开发人员自己负责维护业务逻辑涉及的多层节点之间的关系,例如,某节点与其父节点之间的父子关系、与其子节点之间的父子关系。
3.StAX解析方式:
(图片来源:MyBatis技术内幕)
三、MyBatis初始化解析mybatis-config.xml的解析方式
MyBatis 在初始化过程中处理mybatis-config.xml 配置文件以及映射文件时,使用的是DOM解析方式,井结合使用XPath 解析XML 配置文件。正如前文所述, DOM 会将整个XML 文档加载到内存中并形成树状数据结构,而XPath 是一种为查询XML 文档而设计的语言,它可以与DOM 解析方式配合使用,实现对XML 文档的解析。XPath 之于XML 就好比SQL 语言之于数据库。
四、MyBatis初始化解析mybatis-config.xml的过程
前提:.类路径下有如下xml配置文件
<employee id="${id_var}">
<blah something="that"/>
<first_name>Jim</first_name>
<last_name>Smith</last_name>
<birth_date>
<year>1970</year>
<month>6</month>
<day>15</day>
</birth_date>
<height units="ft">5.8</height>
<weight units="lbs">200</weight>
<active>true</active>
</employee>
1.读取文件并把文件转化为流对象
String resource = "resources/nodelet_test.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
2.使用Xpath解析器
/*解析读取到的资源*/
XPathParser parser = new XPathParser(inputStream, false, null, null);
我们来看一下这一行代码所干的事:
/*解析资源*/
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
/*初始化一些必要的参数*/
commonConstructor(validation, variables, entityResolver);
/*将流资源解析Dom文档*/
this.document = createDocument(new InputSource(inputStream));
}
然后我们再看commonConstructor与createDocument:
(1).commonConstructor:
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
/*初始化校验规则*/
this.validation = validation;
/*初始化实体解析器*/
this.entityResolver = entityResolver;
/*初始化需要解析的变量*/
this.variables = variables;
/*初始化一个XPath工厂*/
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
//xpath = new XPath() ;
}
我们可以看到commonConstructor的主要工作就是初始化一些重要参数,为xpath查找dom文档做准备。
(2)createDocument:
/*此方法的调用必须在 commonConstructor初始化以后*/
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
//这个是DOM解析方式
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
//名称空间
factory.setNamespaceAware(false);
//忽略注释
factory.setIgnoringComments(true);
//忽略空白
factory.setIgnoringElementContentWhitespace(false);
//把 CDATA 节点转换为 Text 节点
factory.setCoalescing(false);
//扩展实体引用
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
//需要注意的就是定义了EntityResolver(XMLMapperEntityResolver),这样不用联网去获取DTD,
//将DTD放在org\apache\ibatis\builder\xml\mybatis-3-config.dtd,来达到验证xml合法性的目的
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 {
}
});
/*至此,把一个普通流资源转化为一个Dom文档流*/
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
至此,我们已经知道了MyBatis是如何加载配置文件的,接下来将会去了解Mybatis 中用Xpath到底是如何查询Dom中的节点数据。