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關閉,寫入二級緩存,返回結束。