1. 準(zhǔn)備工作
? 編寫(xiě)測(cè)試代碼(具體請(qǐng)參考《Mybatis入門(mén)示例》),設(shè)置斷點(diǎn),以Debug模式運(yùn)行,具體代碼如下:?
String resource = "mybatis.cfg.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = ssf.openSession();
???
2.源碼分析
我們此次就對(duì)上面的代碼進(jìn)行跟蹤和分析,let's go。
首先我們按照順序先看看第一行和第二行代碼,看看它主要完成什么事情:
String resource = "mybatis.cfg.xml";
Reader reader = Resources.getResourceAsReader(resource);
????
讀取Mybaits的主配置配置文件,并返回該文件的輸入流,我們知道Mybatis所有的SQL語(yǔ)句都寫(xiě)在XML配置文件里面,所以第一步就需要讀取這些XML配置文件,這個(gè)不難理解,關(guān)鍵是讀取文件后怎么存放。
?
我們接著看第三行代碼(如下),該代碼主要是讀取配置文件流并將這些配置信息存放到Configuration類(lèi)中。
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
?
????
SqlSessionFactoryBuilder的build的方法如下:
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
??
其實(shí)是調(diào)用該類(lèi)的另一個(gè)build方法來(lái)執(zhí)行的,具體代碼如下:
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.
}
}
}
??
我們重點(diǎn)看一下里面兩行:
//創(chuàng)建一個(gè)配置文件流的解析對(duì)象XMLConfigBuilder,其實(shí)這里是將環(huán)境和配置文件流賦予解析類(lèi)
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 解析類(lèi)對(duì)配置文件進(jìn)行解析并將解析的內(nèi)容存放到Configuration對(duì)象中,并返回SqlSessionFactory
return build(parser.parse());
??
這里的XMLConfigBuilder初始化其實(shí)調(diào)用的代碼如下:?
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的parse方法執(zhí)行代碼如下:??
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each MapperConfigParser can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
?
解析的內(nèi)容主要是在parseConfiguration方法中,它主要完成的工作是讀取配置文件的各個(gè)節(jié)點(diǎn),然后將這些數(shù)據(jù)映射到內(nèi)存配置對(duì)象Configuration中,我們看一下parseConfiguration方法內(nèi)容:?
private void parseConfiguration(XNode root) {
try {
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
propertiesElement(root.evalNode("properties"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
最后的build方法其實(shí)是傳入配置對(duì)象進(jìn)去,創(chuàng)建DefaultSqlSessionFactory實(shí)例出來(lái). DefaultSqlSessionFactory是SqlSessionFactory的默認(rèn)實(shí)現(xiàn).?
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
???
?最后我們看一下第四行代碼:?
SqlSession session = ssf.openSession();
?
通過(guò)調(diào)用DefaultSqlSessionFactory的openSession方法返回一個(gè)SqlSession實(shí)例,我們看一下具體是怎么得到一個(gè)SqlSession實(shí)例的。首先調(diào)用openSessionFromDataSource方法。??
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
?
?下面我們看一下openSessionFromDataSource方法的邏輯:??
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Connection connection = null;
try {
//獲取配置信息里面的環(huán)境信息,這些環(huán)境信息都是包括使用哪種數(shù)據(jù)庫(kù),連接數(shù)據(jù)庫(kù)的信息,事務(wù)
final Environment environment = configuration.getEnvironment();
//根據(jù)環(huán)境信息關(guān)于數(shù)據(jù)庫(kù)的配置獲取數(shù)據(jù)源
final DataSource dataSource = getDataSourceFromEnvironment(environment);
//根據(jù)環(huán)境信息關(guān)于事務(wù)的配置獲取事務(wù)工廠(chǎng)
TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
connection = dataSource.getConnection();
if (level != null) {
//設(shè)置連接的事務(wù)隔離級(jí)別
connection.setTransactionIsolation(level.getLevel());
}
//對(duì)connection進(jìn)行包裝,使連接具備日志功能,這里用的是代理。
connection = wrapConnection(connection);
//從事務(wù)工廠(chǎng)獲取一個(gè)事務(wù)實(shí)例
Transaction tx = transactionFactory.newTransaction(connection, autoCommit);
//從配置信息中獲取一個(gè)執(zhí)行器實(shí)例
Executor executor = configuration.newExecutor(tx, execType);
//返回SqlSession的一個(gè)默認(rèn)實(shí)例
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeConnection(connection);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
?
傳入?yún)?shù)說(shuō)明:
(1)ExecutorType:執(zhí)行類(lèi)型,ExecutorType主要有三種類(lèi)型:SIMPLE, REUSE, BATCH,默認(rèn)是SIMPLE,都在枚舉類(lèi)ExecutorType里面。
(2)TransactionIsolationLevel:事務(wù)隔離級(jí)別,都在枚舉類(lèi)TransactionIsolationLevel中定義。
(3)autoCommit:是否自動(dòng)提交,主要是事務(wù)提交的設(shè)置。
?
?DefaultSqlSession是SqlSession的實(shí)現(xiàn)類(lèi),該類(lèi)主要提供操作數(shù)據(jù)庫(kù)的方法給開(kāi)發(fā)人員使用。
??
這里總結(jié)一下上面的過(guò)程,總共由三個(gè)步驟:
步驟一:讀取Ibatis的主配置文件,并將文件讀成文件流形式(InputStream)。
?
步驟二:從主配置文件流中讀取文件的各個(gè)節(jié)點(diǎn)信息并存放到Configuration對(duì)象中。讀取mappers節(jié)點(diǎn)的引用文件,并將這些文件的各個(gè)節(jié)點(diǎn)信息存放到Configuration對(duì)象。
?
步驟三:根據(jù)Configuration對(duì)象的信息獲取數(shù)據(jù)庫(kù)連接,并設(shè)置連接的事務(wù)隔離級(jí)別等信息,將經(jīng)過(guò)包裝數(shù)據(jù)庫(kù)連接對(duì)象SqlSession接口返回,DefaultSqlSession是SqlSession的實(shí)現(xiàn)類(lèi),所以這里返回的是DefaultSqlSession,SqlSession接口里面就是對(duì)外提供的各種數(shù)據(jù)庫(kù)操作。