java架構之路-(mybatis源碼)mybatis執行流程源碼解析
- 2019 年 10 月 3 日
- 筆記
這次我們來說說Mybatis的源碼,這裡只說執行的流程,內部細節太多了,這裡只能授之以漁了。還是最近的那段代碼,我們來回顧一下。
package mybatis; import mybatis.bean.StudentBean; import mybatis.dao.StudentMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class Test1 { public SqlSession session; public SqlSessionFactory sqlSessionFactory; @Before public void init() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sqlSessionFactory.openSession(); } @Test public void studentTest() { StudentMapper mapper = session.getMapper(StudentMapper.class); StudentBean result = mapper.selectUser(1); System.out.println(result); session.commit(); } }
就是拿到流文件,也是我們主配置文件,進行流文件解析,傳入到build內,構建成一個sqlSessionFactory,再由sqlSessionFactory得到session,拿到mapper,執行sql,完成。
簡單的流程應該是這樣的,我們來一個稍微專業一點圖。
轉變為我們的代碼大概就是這三步,構建Configuration對象,構建我們的sqlsessionfactory,得到我們的session,得到對應的statement(處理參數和結果)從而得到結果集。
那麼我們先從我們一步步來看,先看我們的Configuration對象
Configuration解析:
我們打開Configuration類可以看到,裏面很多的屬性設置,包括緩存,mapper,插件等等,其實就是把我們的xml標籤轉化為對象了,這裡需要說明一下的是,這個解析過程是把所有的相關的xml都轉為Configuration對象了,包括config.xml和mapper.xml。源碼太多,我就不粘貼了。我給你看一下我轉化完成的。
這裡沒有什麼神秘的,就是一個xml解析的過程,在XMLConfigBuilder類的parse方法,生成了完成的Configuration對象,有興趣的可以打個斷點看一下。
這裡要注意的就是裏面很多元素是一個對應多個的,很多屬性是map和set。順便收一下裏面是由多個構造器來構建的。
MapperAnnotationBuilder為註解方式的構造器,其餘都是為Configuration服務的。也就是說在創建Configuration之後很多東西就已經確定了(除了cache)。那麼又是如何生成sqlsession的呢。回到我們創建完Configuration對象那句源碼上來。
點擊build方法進去,我們看到是new DefaultSqlSessionFactory然後把我們的Configuration對象傳遞進去,也就是說有了Configuration對象也就生成了我們的DefaultSqlSessionFactory對象。DefaultSqlSessionFactory實現了我們的SqlSessionFactory,我們也就得到了SqlSessionFactory。接下來就是我們的sqlsession了
sessionsql解析:
還是老規矩,上個圖再看源碼,比較好理解。
執行過程大概是這樣的。
就是什麼意思呢?由DefaultSqlSessionFactory生成一個執行器Executor,下面是由BaseExecutor支撐的,裏面包含一些配置和我們的一級緩存(是不是更深入的知道為啥一級緩存生命短了),同時也有一個類似裝飾器的CachingExecutor,他的下面也是需要BaseExecutor來支撐的,需要查詢時優先查緩存,查詢不到回到我們的BaseExecutor來執行。摟一眼源碼去,sqlSessionFactory.openSession()方法也就得到了我們的sqlsession,我們進去看一下都寫了什麼。
裏面的95行tx主要是從我們的configuration中拿到一些數據源的配置,也就是我們圖中畫的來支撐BaseExecutor的配置,來到96行,創建Executor,傳入了數據源配置和一個執行類型,這裡簡單提一嘴,類型主要有三種:SIMPLE(簡單), REUSE(可重複使用), BATCH(批量進行),一般我們都用的SIMPLE。我們再進我們的Executor的創建方法看看,他們都做了什麼事。
先判斷了執行器類型,簡單,可重複,批量,613行就是我們的緩存執行器了,外面那個判斷就是你是否配置了二級緩存,從而是否配置我們的緩存執行器,底層還是Executor,可以點擊進去看看的。
就是說我們由配置文件config.xml,mapper.xml生成了我們的Configuration對象,將Configuration放置在DefaultSqlSessionFactory內,生成了我們的Executor執行器,準備執行SQL。回到主題我們繼續看下一部分,最後一個MappedStatement
MappedStatement解析:
記住兩個問題,增刪改查,可以歸類為兩種,一種是原來的數據變化-增刪改,另一種是原來的數據沒有變化-查詢。我們的mybatis也是這樣來處理的,主要就是query和update兩種大類方法。
MappedStatement是由調用Executor執行器前,由configuration對象來構建的。源碼在DefaultSqlSession的select***方法內,這裡就不詳細說了。感興趣的可以自己去了解一下。我們主要來說執行流程。
執行過程大致是,1.拿到sql;2.拼接參數;3.執行sql;4.封裝結果集。我們來看一下具體的源碼流程。
我們上面得知,執行器的真正執行都是在BaseExecutor里來執行的,我們在DefaultSqlSession調用的select方法,最後執行了executor.query,只優先走我們的CachingExecutor執行器,如果沒有才到我們的BaseExecutor執行器裏面來,我們的sql是查詢,不需要變動數據,那麼我們把斷點打在我們的BaseExecutor類的query方法上。
147行是清理我們的一級緩存,如果在mapper.xml配置了清理標籤,這裡會先清理一級緩存。queryStack表示這個執行器是否正在被使用。
152行開始查詢我們的一級緩存,如果為空調用156行,開始我們正式的查詢,裏面是放入緩存佔位置,然後執行查詢,清理緩存的佔位重新放置緩存,這裡說的都是一級緩存了。
總結一下就是:
1.拿到流文件config.xml和mapper.xml;
2.用我們的兩個或多個流文件創建一個Configuration對象,(單一職責原則來構建的,很多個構造器來構建的,例如XMLConfigBuilder)
3.將Configuration對象塞給SqlSessionFactoryBuilder類的build方法,構建SqlSessionFactory對象。
4.sqlSessionFactory.openSession()拿到我們的session對象。
5.由session對象和Configuration對象封裝參數和結果集映射,產生對應的執行器來,二級緩存執行器和BaseExecutor執行器。
6.由二級緩存執行器CachingExecutor來優先查詢二級緩存是否存在,不存在執行BaseExecutor執行器的query或update方法。
7.拿到結果集轉換ResultMap,session關閉,寫入二級緩存,返回結束。