@
1. 簡介
在之前的文章《mybatis 初步使用(IDEA的Maven項目, 超詳細)》中, 講解了mybatis的初步使用, 並總結了以下mybatis的執行流程:
- 通過 Resources 工具類讀取 mybatis-config.xml, 存入 Reader;
- SqlSessionFactoryBuilder 使用上一步獲得的 reader 創建 SqlSessionFactory 對象;
- 通過 sqlSessionFactory 對象獲得 SqlSession;
- SqlSession對象通過 *Mapper 方法找到對應的 SQL 語句, 執行 SQL 查詢。
- 底層通過 JDBC 查詢后獲得 ResultSet, 對每一條記錄, 根據resultMap的映射結果映射到 Student 中, 返回 List。
- 最后記得關閉 SqlSession
本系列文章深入講解第 2 步, 解析配置文件。
2. 配置文件解析流程分析
2.1 調用
配置文件的解析過程對應的是以下的代碼:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
很簡單的兩句代碼:
- 通過
mybatis的資源類Resources讀入“mybatis-config.xml”文件; - 使用
SqlSessionFactoryBuilder類生成我們需要的SqlSessionFactory類;(真正的解析只有這一過程)
2.2 解析的目的
要理解配置文件的解析過程, 首先要明白解析的目的是什么, 從最直觀的調用代碼來看, 是獲得SqlSessionFactory。
但是, 從源代碼來看, 更本質的應該這么說:
mybatis解析配置文件最本質的目的是為了獲得
Configuration對象
Configuration 對象, 可以理解是mybatis的XML文件在程序中的化身。
2.3 XML 解析流程
build(reader)函數里面包含着SqlSessionFactory的創建邏輯。
從客戶端調用build(reader)函數到返回SqlSessionFactory, 可以用如下的時序圖表示:

下面來看看各個步驟, 請記住,mybatis解析配置文件的本質就是獲得Configuration對象。
2.3.1 build(parser)
其最終調用以下的方法
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
該方法:
- 創建
XMLConfigBuilder對象; - 使用
XMLConfigBuilder對象的方法parse()來獲得Confiuration對象; - 通過
build(configuration), 使用Confiuration對象創建相應的SqlSessionFactory對象。
2.3.2 new XMLConfigBuilder(...);
new XMLConfigBuilder(reader, environment, properties)方法, 從字面上來理解就是創建一個XMLConfigBuilder對象。
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
其最終調用的方法是這個:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
XMLConfigBuilder類繼承於BaseBuilder類, super(new Configuration())對應的方法:
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
也就是給BaseBuilder類的各個成員變量賦值而已。
里面的XpathParser對象是通過new XPathParser(reader, true, props, new XMLMapperEntityResolver())方法而來的。
2.3.3 new XPathParser(...)
new XPathParser(reader, true, props, new XMLMapperEntityResolver())就是創建XpathParser的過程。
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
調用了以下兩個函數:
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);
}
}
注意這兩個函數是有先后順序的, createDocument函數務必在commonConstructor函數之后執行。
createDocument函數, 其實就是通過 DOM 解析 XML 文件的過程中的幾個步驟,獲得document, 具體可以參見 「mybatis 解析配置文件(一)之XML的DOM解析方式」, 里面提到了 Java 中使用 DOM 解析 XML 的步驟, 大致如下:
- 創建
DocumentBuilderFactory對象;- 通過
DocumentBuilderFactory創建DocumentBuilder對象;- 通過
DocumentBuilder, 從文件或流中創建通過Document對象;- 創建
XPathFactory對象, 並通過XPathFactory創建XPath對象;- 通過
XPath解析出XPathExpression對象;- 使用
XPathExpression在文檔中搜索出相應的節點。
剛剛提到的兩個函數, 已經完成了前4部分, 獲得了Document對象, Xpath對象, 並返回后將其賦值給了相應的成員變量。
也就是說, 到了這一步, 我們已經獲得了XpathParser對象, 該對象中已經含有 mybatis-config.xml 文件對應的 Document Object, 即document和xpath。 通過document和xpath,我們可以對 mybatis-config.xml 進行后兩部操作操作。
后面幾個步驟, 是在XMLConfiguration對象的parse()函數中使用到, 詳情見 2.3.5。
2.3.4 new Configuration()
之前提到過, 配置文件解析的本質就是獲得Configuration對象。
現在, Configuration對象在解析的過程中第一次出現了。
那我們就可以返回這個對象了?
當然不是, 這個對象現在只是創建, 后續還有很多成員變量需要根據 XML 配置文件解析后來賦值。
2.3.5 parser.parse()
這里的parser是XMLConfigBuilder對象。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
這個函數返回的Configuration對象就是最終寫入SqlSessionFatory對應成員變量的對象。
由於配置文件解析的本質就是獲得Configuration對象, 因此, 這個函數就是解析的核心。
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);
}
}
其對應的過程就是解析 XML 配置文件中 properties, settings, typeAliases, plugins, objectFactory, objectWrapperFactory, reflectorFactory, environments, databaseIdProvider, typeHandlers, mappers, 這些子節點。
其中的evalNode函數, 在其函數過程中, 會調用XParhParser中的函數, 對 xml 節點進行解析:
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);
}
}
以上過程就是我們 2.3.3 中提到的第 5, 6 步過程。
具體的在后續的文章中在深入了解。
2.3.6 build(configuration)
該函數就是創建一個具體的SqlSessionFactory對象。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
就是創建DefaultSqlSessionFactory對象, 並將configuration賦值給相應的成員變量。
更具體的解析配置的過程, 后續分享。
一起學 mybatis
你想不想來學習 mybatis? 學習其使用和源碼呢?那么, 在博客園關注我吧!!
我自己打算把這個源碼系列更新完畢, 同時會更新相應的注釋。快去 star 吧!!

