我們知道使用MyBatis前是需要初始化的,我們來看一段代碼:
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List list = sqlSession.selectList("edu.zhwei.mapper.sqlId");
上述代碼所經歷的階段:
1.讀取配置文件並創建SqlSessionFactory
2.獲得sqlSession
3.執行查詢
而MyBatis的初始化就在階段1中,更準確的說是在第三行代碼,根據配置文件,創建立SqlSessionFactory對象。那就讓我們看一看這一行代碼究竟發生了什麼。
SqlSessionFactoryBuilder
相關源碼:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//創建XMLConfigBuilder對象,
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//parser.parse()方法返回一個Configuration 對象,這裏需要說明一下。
//返回SqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
//根據Configuration對象,返回DefaultSqlSessionFactory
return new DefaultSqlSessionFactory(config);
}
說明:根據傳入的inputStream, environment, properties創建XMLConfigBuilder對象,XMLConfigBuilder.parse()用來將inputStream創建成一個Configuration對象,然後在調用build(Configuration config)方法返回DefaultSqlSessionFactory。
那麼parse()方法是如何實現的呢?
XMLConfigBuilder類
parse()方法源碼:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//parser.evalNode("/configuration")返回一個XNode 對象
//parseConfiguration(XNode)將配置文件中配置的信息解析並設置到Configuration對象中
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//解析各種類型的節點,如properties、typeAliases、plugins等
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
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);
}
}
說明:parser.evalNode(“/configuration”)返回一個XNode 對象,我們看一下evalNode方法,我們發現XNode是取自XPathParser的。
XPathParser.evalNode源碼:
public XNode evalNode(String expression) {
return evalNode(document, expression);
}
public XNode evalNode(Object root, String expression) {
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
}
return new XNode(this, node, variables);
}
XPathParser類:
public class XPathParser {
//document對象
private Document document;
private boolean validation;
//entityResolver對象
private EntityResolver entityResolver;
private Properties variables;
private XPath xpath;
........
}
我們需要關注一下document對象和entityResolver對象。
document對象:包含了XML配置信息。
entityResolver:包含了XML中的DTD信息。
因此,XMLConfigBuilder調用parse()方法:會從XPathParser中取出 configuration節點對應的XNode對象,然後解析此XNode節點的子,產生Configration對象。
至此,XMLConfigBuilder的parse()就分析完成了。
重新修改
我們再來看第三行代碼
//看這裏
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//將第三行代碼改爲如下三行代碼
//第一步創建XMLConfigBuilder
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null,null);
//第二步:執行parse()得到代表配置文件的Configuration對象
Configuration config = parser.parse();
//根據Configuration創建DefaultSqlSessionFactory2
SqlSessionFactory = new DefaultSqlSessionFactory(config);
MyBatis的初始化就是上述過程。