最近闲来无事,看着一些源码类的书籍,只是光看,好像并不能给自己很好的益处,无法记下来,所以就有了这个Mybatis源码解析系列的博客。网上也有大量的源码解析,在此记录有两个原因,一是为了加深自己的印象,二来则是让广大读者朋友能及时纠正在下的一些理解的错误。如果各位发现有任何的错误,请留言指出,在此感激不尽。闲话不多说,这就开始我们的第一篇xml解析。
首先,本博客系列的图、代码,大多来自《mybatis技术内幕》一书。
这一讲主要是对解析器模块一个简单分析。
那么就先由一个简单的Mybatis启动开始讲解吧。
也请大家下载mybatis源码(或者在https://github.com/mybatis/mybatis-3中找到对应的源码),本博客中的例子大部分来自己mybatis中的test包。我尽量减少在里面放置过多的源码。
https://github.com/mybatis/mybatis-3/blob/master/src/test/java/org/apache/ibatis/session/SqlSessionTest.java#L64
public static void setup() throws Exception {
createBlogDataSource();
final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
final Reader reader = Resources.getResourceAsReader(resource);
sqlMapper = new SqlSessionFactoryBuilder().build(reader);
}
在此处加载了mybatis的config文件,大家在mapperConfig.xml(https://github.com/mybatis/mybatis-3/blob/master/src/test/java/org/apache/ibatis/builder/MapperConfig.xml)中可以看到内容的。
在Build方法调用时会创建相应的xpath对象
https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/parsing/XPathParser.java#L229
createDocument方法中创建了xpath。
现在我们来说说xpath的API解析XML。
package org.apache.ibatis.test;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.io.Resources;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.Reader;
public class XPathTest {
private Document createDocument(InputSource inputSource) {
boolean validation = true;
EntityResolver entityResolver = new XMLMapperEntityResolver();
// 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);
}
}
public static void main(String[] args) throws IOException, XPathExpressionException {
final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
final Reader reader = Resources.getResourceAsReader(resource);
XPathTest test = new XPathTest();
Document document = test.createDocument(new InputSource(reader));
XPathFactory factory = XPathFactory.newInstance();
XPath xPath = factory.newXPath();
Node nodes = (Node) xPath.evaluate("/configuration", document, XPathConstants.NODE);
// 获取指定节点
Node mappers = (Node) xPath.evaluate("mappers", nodes, XPathConstants.NODE);
NodeList mapperList = mappers.getChildNodes();
for(int i=0; i 0)
parse(childNodes);
}
}
public static String getAttribute(Node node, String attributeName){
NamedNodeMap nodeAttributes = node.getAttributes();
for (int i= 0; i< nodeAttributes.getLength(); i ++){
Node attribute = nodeAttributes.item(i);
String name = attribute.getNodeName();
String value = attribute.getNodeValue();
if(attributeName.equalsIgnoreCase(name)){
return value;
}
}
return null;
}
}
这是一个简单的xpath的解析xml的例子,与Mybatis中获取xml配置的解析差不多。
由此我们知道了xml的各节点是以该方式解析出来的。
现在我们来看看XPathParse类,该类就是封装了解析xml的api的。
XPathParse类中的字段说明:
private final Document document; //就是我们上述XPathTest中的document对象
private boolean validation; //是否开启验证
private EntityResolver entityResolver;//用于加载本地的DTD文件(DTD是用来验证xml的合法性的)
private Properties variables; //用于记录mybatis-config.xml中的变量的键值对的,比如在xml中有${password}等变量时,会由该字段保留具体值。
private XPath xpath;// 与XPathTest中的xpath一致,用于解析xml
XNode对象是对于XPath中的获取的Node对象的一个封装,这里就不过多的讲解,各位可以去看该类的源码。
在XMLConfigBuilder类中的https://github.com/mybatis/mybatis-3/blob/master/src/main/java/org/apache/ibatis/builder/xml/XMLConfigBuilder.java#L102
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
此方法中的root.evalNode中获取的XNode对象就是对XPath中的Node对象的一个封闭。
在此xml解析就大致完了,下一节应该会是反射相关的分析。
mybatis中大量用了反射来将Mapper接口中的方法参数与返回值做解析。