1. 程式人生 > >java架構之路-(原始碼)mybatis執行流程原始碼解析

java架構之路-(原始碼)mybatis執行流程原始碼解析

  這次我們來說說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關閉,寫入二級快取,返回結束。

&nbs