Mybatis 源碼分析(一)之 Mybatis 的Executor的初始化
Mybatis系列:
Mybatis 基礎(chǔ)介紹與逆向工程的構(gòu)建 :http://www.reibang.com/p/1c18db4d7a38
Mybatis 源碼分析(一)之 Mybatis 的Executor的初始化:http://www.reibang.com/p/c7425c841337
Mybatis 源碼分析(二)之 Mybatis 操作數(shù)據(jù)庫的流程 :http://www.reibang.com/p/11d354ec3612
Mybatis 源碼分析(三)之 Mybatis 的一級(jí)緩存和二級(jí)緩存 :http://www.reibang.com/p/5515640d14fe
先看一下mybatis中幾個(gè)核心的概念名詞
名稱 | 意義 |
---|---|
Configuration | 管理 mybatis-config.xml 全局配置關(guān) |
SqlSessionFactor | Session 管理工廠 |
Session | SqlSession 是一個(gè)面向用戶(程序員)的接口叼丑。SqlSession中提供了很多操作數(shù)據(jù)庫的方法 |
Executor | 執(zhí)行器是一個(gè)接口(基本執(zhí)行器、緩存執(zhí)行器)作用:SqlSession內(nèi)部通過執(zhí)行器操作數(shù)據(jù)庫 |
MappedStatement | 底層封裝對(duì)象作用:對(duì)操作數(shù)據(jù)庫存儲(chǔ)封裝,包括sql語句屎勘、輸入輸出參數(shù) |
StatementHandle | 具體操作數(shù)據(jù)庫相關(guān)的 handler 接口 |
ResultSetHandler | 具體操作數(shù)據(jù)庫返回結(jié)果的 handler |
看下mybatis的源碼結(jié)構(gòu):
├─annotations ->注解相關(guān) 比如 select insert
├─binding -> mapper 相關(guān)
├─builder ->解析 xml 相關(guān)
├─cache ->緩存
├─cursor -> 返回結(jié)果 resultset
├─datasourcer ->數(shù)據(jù)管理
├─exceptionsr -> 異常
├─executorr -> 執(zhí)行器
├─io ->classloader
├─jdbc ->jdbc
├─lang ->jdk7 jdk8
├─logging ->日志相關(guān)
├─mapping ->mapper 相關(guān)的封裝
├─parsing ->xml 相關(guān)解析
├─plugin ->攔截器
├─reflection ->反射相關(guān)
├─scripting ->數(shù)據(jù)廠家
├─session ->sessiomn
├─transaction ->事務(wù)
└─type ->返回類型
具體查看方法钟鸵,可以先下載一下maven的源碼:https://github.com/mybatis/mybatis-3/releases
然后進(jìn)入導(dǎo)入我們的IDE中性昭,然后進(jìn)入到j(luò)ava目錄渣蜗,進(jìn)入到終端(Terminal)里面輸入tree,IDE已經(jīng)集成Maven屠尊,所以就不需要mvn命令了。具體如下耕拷,上面是整理版的讼昆。
└─java
└─org
└─apache
└─ibatis
├─annotations
├─binding
├─builder
│ ├─annotation
│ └─xml
├─cache
│ ├─decorators
│ └─impl
├─cursor
│ └─defaults
├─datasource
│ ├─jndi
│ ├─pooled
│ └─unpooled
├─exceptions
├─executor
│ ├─keygen
│ ├─loader
│ │ ├─cglib
│ │ └─javassist
│ ├─parameter
│ ├─result
│ ├─resultset
│ └─statement
├─io
├─jdbc
├─lang
├─logging
│ ├─commons
│ ├─jdbc
│ ├─jdk14
│ ├─log4j
│ ├─log4j2
│ ├─nologging
│ ├─slf4j
│ └─stdout
├─mapping
├─parsing
├─plugin
├─reflection
│ ├─factory
│ ├─invoker
│ ├─property
│ └─wrapper
├─scripting
│ ├─defaults
│ └─xmltags
├─session
│ └─defaults
├─transaction
│ ├─jdbc
│ └─managed
└─type
拿我們之前寫的demo,查看源碼里面是怎么寫的骚烧,如下面這個(gè)浸赫。
@Test
public void test01() throws IOException {
String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
User user = session.selectOne("selectByPrimaryKey", 1);
session.commit();
logger.info(user.toString());
}
這個(gè)很好理解,以流的方式讀取我們的config文件赃绊。
Reader reader = Resources.getResourceAsReader(resource);
然后來看SqlSessionFactory是怎么build創(chuàng)建的既峡。(下面只展示核心代碼)
new SqlSessionFactoryBuilder().build(reader);
進(jìn)入看一下。
public SqlSessionFactory build(Reader reader) {
return this.build((Reader)reader, (String)null, (Properties)null);
}
繼續(xù)進(jìn)入
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
SqlSessionFactory var5 = this.build(parser.parse());
return var5;
}
這時(shí)候可以看到實(shí)例化了一個(gè)核心的類XMLConfigBuilder碧查,然后parser.parse()运敢,進(jìn)行解析,我們點(diǎn)進(jìn)去么夫。
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
重點(diǎn)看這個(gè)
this.parseConfiguration(this.parser.evalNode("/configuration"));
繼續(xù)點(diǎn)進(jìn)去
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectionFactoryElement(root.evalNode("reflectionFactory"));
this.settingsElement(root.evalNode("settings"));
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
OK者冤,就是我們之前說的mybatis-config.xml里面的屬性肤视。
mybatis-config.xml
屬性名 | 作用 |
---|---|
屬性(peoperties) | 系統(tǒng)屬性占用配置 |
設(shè)置(settings) | 用于修改Mybatis的運(yùn)行時(shí)行為 |
類型別名(typeAliases) | 為類型建立別名档痪,一般使用更短的名稱代替 |
類型處理器(typeHanders) | 用于將預(yù)編譯語句(PreparedStatement)或者結(jié)果集(ResultSet)中的JDBC類型轉(zhuǎn)換為Java類型 |
對(duì)象工廠(ObjectFactory) | 提供默認(rèn)構(gòu)造器或執(zhí)行構(gòu)造器參數(shù)初始化目標(biāo)類型的對(duì)象 |
插件(plugins) | Mybatis提供插件的方式來攔截映射(可以根據(jù)自己的需求進(jìn)行編寫插件) |
環(huán)境(environments) | Mybatis運(yùn)行配置多個(gè)環(huán)境 |
數(shù)據(jù)庫標(biāo)識(shí)提供商(databaseIdProvider) | 數(shù)據(jù)庫標(biāo)識(shí)提供商 |
SQL映射文件(mappers) | SQL映射文件 |
中間有將我們的配置封裝Node對(duì)象
public XNode evalNode(String expression) {
return this.evalNode(this.document, expression);
}
public XNode evalNode(Object root, String expression) {
Node node = (Node)this.evaluate(expression, root, XPathConstants.NODE);
return node == null ? null : new XNode(this, node, this.variables);
}
在之后,返回一個(gè)DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
整體的一個(gè)調(diào)用鏈?zhǔn)沁@樣的
org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
>org.apache.ibatis.builder.xml.XMLConfigBuilder 實(shí)例構(gòu)造函數(shù)
>org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
>org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration 解析mybatis-config.xml中的內(nèi)容
>org.apache.ibatis.parsing.XPathParser.evaluate
>org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement
>org.apache.ibatis.session.SqlSessionFactoryBuilder.build(org.apache.ibatis.session.Configuration)
>org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.DefaultSqlSessionFactory 返回一個(gè)DefaultSqlSessionFactory
整個(gè)流程就是返回一個(gè)我們下一步需要的DefaultSqlSessionFactory邢滑。
下一段代碼
SqlSession session = sqlSessionFactory.openSession();
同樣的腐螟,一步步點(diǎn)進(jìn)去
public SqlSession openSession() {
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
繼續(xù)
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
其中 transactionFactory.newTransaction 返回一個(gè)TransactionFactory
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
然后
Executor executor = this.configuration.newExecutor(tx, execType);
返回一個(gè)執(zhí)行器 Executor
Executor:執(zhí)行器是一個(gè)接口(基本執(zhí)行器、緩存執(zhí)行器)
作用:SqlSession內(nèi)部通過執(zhí)行器操作數(shù)據(jù)庫
然后繼續(xù)進(jìn)去
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
//批處理執(zhí)行器
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
//可重復(fù)用的執(zhí)行器
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//cacheEnabled默認(rèn)是true困后,傳入的參數(shù)是SimpleExecutor乐纸,裝飾成CachingExecutor
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
//這個(gè)是攔截器鏈,責(zé)任鏈模式攔截器摇予,不過需要我們自己實(shí)現(xiàn)
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
最后返回給我們一個(gè)executor汽绢,因?yàn)閙ybatis默認(rèn)開啟緩存,所以返回是CachingExecutor侧戴,但是如果沒有在mybatis-config.xml中開啟二級(jí)緩存宁昭,在后面的query查詢中MappedStatement.getCache()時(shí)跌宛,返回的還是空的,在之后用delegate.query(...)進(jìn)行查詢积仗,那時(shí)executor是SimpleExecutor疆拘。
Executor類圖
如有問題,歡迎留言:)