前言
- 前一篇文章簡單的介紹了Mybatis的六個重要元件,這六劍客佔據了Mybatis的半壁江山,和六劍客搞了基友,那麼Mybatis就是囊中之物了。對六劍客感興趣的朋友,可以看看這篇文章:Mybatis原始碼解析篇之六劍客
- 有些初入門的朋友可能很害怕閱讀原始碼,不知道如何閱讀原始碼,與其我一篇文章按照自己的思路寫完Mybatis的原始碼,但是你們又能理解多少呢?不如教會你們思路,讓你們能夠自己知道如何閱讀原始碼。
環境配置
- 本篇文章講的一切內容都是基於
Mybatis3.5
和SpringBoot-2.3.3.RELEASE
。
從哪入手?
-
還是要說一說六劍客的故事,既然是Mybatis的重要元件,當然要從六劍客下手了,沿用上篇文章的一張圖,此圖記錄了六劍客先後執行的順序,如下:
-
閱讀原始碼最重要的一點不能忘了,就是開啟
DEBUG
模式,重要方法打上斷點,重要語句打上斷點,先把握整體,再研究細節,基本就不難了。 -
下面就以Myabtis的查詢語句
selectList()
來具體分析下如何閱讀。
總體把握六劍客
-
從六劍客開整,既然是重要元件,原始碼執行流程肯定都是圍繞著六劍客,下面來對六劍客一一分析,如何打斷點。
-
下面只是簡單的教你如何打斷點,對於六劍客是什麼不再介紹,請看上篇文章。
SqlSession
- 既然是介面,肯定不能在介面方法上打斷點,上文介紹有兩個實現類,分別是
DefaultSqlSession
、SqlSessionTemplate
。那麼SpringBoot在初始化的時候到底注入的是哪一個呢?這個就要看Mybatis的啟動器的自動配置類了,其中有一段這樣的程式碼,如下:
//如果容器中沒有SqlSessionTemplate這個Bean,則注入
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
-
從上面的程式碼可以知道,SpringBoot啟動時注入了
SqlSessionTemplate
,此時就肯定從SqlSessionTemplate
入手了。它的一些方法如下圖:
-
從上圖的標記可以知道,首當其衝的就是
構造方法
了;既然是分析selectList()
的查詢流程,當然全部的selectList()
方法要打上斷點了;上篇文章也講了Mapper的介面最終是走的動態代理生成的例項,因此此處的getMapper()
也打上斷點。 -
對於初入門的來說,上面三處打上斷點已經足夠了,但是如果你仔細看一眼
selectList()
方法,如下:
@Override
public <E> List<E> selectList(String statement) {
//此處的sqlSessionProxy是什麼,也是SqlSession型別的,此處斷點執行到這裡可以知道,就是DefaultSqlSession例項
return this.sqlSessionProxy.selectList(statement);
}
sqlSessionProxy
是什麼,沒關係,這個不能靠猜,那麼此時斷點走一波,走到selectList()
方法內部,如下圖:
- 從上圖可以很清楚的看到了,其實就是
DefaultSqlSession
。哦,明白了,原來SqlSessionTemplate把過甩給了DefaultSqlSession
了,太狡詐了。 DefaultSqlSession
如何打斷點就不用說了吧,自己搞搞吧。
Executor
-
上面文章講過執行器是什麼作用,也講過Mybatis內部是根據什麼建立執行器的。此處不再贅述了。
-
SpringBoot整合各種框架有個特點,萬變不離自動配置類,框架的一些初始化動作基本全是在自動配置類中完成,於是我們在配置類找一找在哪裡注入了
Executor
的Bean,於是找到了如下的一段程式碼:
-
從上面的程式碼可以知道預設建立了
CachingExecutor
,二級快取的執行器,別管那麼多,看看它重寫了Executor
的哪些介面,與selectList()
相關的方法打上斷點,如下圖:
-
從上圖也知道哪些方法和
selectList()
相關了,顯然的query
是查詢的意思,別管那麼多,先打上斷點。 -
此時再仔細瞅一眼
query()
的方法怎麼執行的,哦?發現了什麼,如下:
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//先嚐試從快取中獲取
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//沒有快取,直接呼叫delegate的query方法
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
- 從上面的程式碼知道,有快取了,直接返回了,沒有快取,呼叫了
delegate
中的query
方法,那麼這個delegate
是哪個類的物件呢?參照sqlSession的分析的方法,除錯走起,可以知道是SimpleExecutor
的例項,如下圖:
- 後面的
SimpleExecutor
如何打斷點就不再說了,自己嘗試找找。
StatementHandler
- 很熟悉的一個介面,在學JDBC的時候就接觸過類似的,執行語句和設定引數的作用。
- 這個介面很簡單,大佬寫的程式碼,看到方法名就知道這個方法是幹什麼的,如下圖:
- 最重要的實現類是什麼?當然是
PreparedStatementHandler
,因此在對應的方法上打上斷點即可。
ParameterHandler
- 這個介面很簡單,也別選擇了,總共兩個方法,一個設定,一個獲取,在實現類
DefaultParameterHandler
中對應的方法上打上斷點即可。
TypeHandler
- 型別處理器,也是一個簡單的介面,總共'兩個'方法,一個設定引數的轉換,一個對結果的轉換,啥也別說了,自己找到對應引數型別的處理器,在其中的方法打上斷點。
ResultSetHandler
- 結果處理器,負責對結果的處理,總共三個方法,一個實現類
DefaultResultSetHandler
,全部安排斷點。
總結
- 授人以魚不授人以漁,與其都分析了給你看,不如教會你閱讀原始碼的方式,先自己去研究,不僅僅是閱讀Mybatis的原始碼是這樣,閱讀任何框架的原始碼都是如此,比如Spring的原始碼,只要找到其中重要的元件,比如前置處理器,後置處理器,事件觸發器等等,一切都迎刃而解。
- 如果你覺得作者寫的不錯,有所收穫,不妨關注分享一波,後續更多精彩內容更新。