Mybatis工作原理分析-MyBatis源碼解析

1.Mybatis的框架設計Mybatis分層

Mybatis總共分爲四層,接口層、數據處理層、框架支持層、引導層。

  1. 接口層-和數據庫進行交互的方式。用戶通過接口層來進行數據庫的增刪改查操作。與數據庫進行交互的方式有兩種:a.使用傳統的MyBatis提供的API;b. 使用Mapper接口。
  2. 數據處理層。數據處理層底層是基於JDBC的。包括,解析sql的參數;拿到sql語句,預編譯sql語句;sql的執行;處理sql執行的結果,這些過程都是原生JDBC的流程。
  3. 框架支持層。讀取sql配置文件;支持事務管理、連接池管理、緩存機制。
  4. 引導層。讀取全局配置文件啓動Mybatis。

2.Mybatis工作原理-源碼分析

分析Mybatis的源碼,可以從基本的hello world開始,hello world主要包含下面四個步驟,下面將分別結合源碼分別進行分析:

  1. 獲取sqlSessionFactory對象
  2. 獲取sqlSession對象
  3. 獲取Mapper的代理對象(MapperProxy)
  4. 執行增刪改查方法

2.1 創建sqlSessionFactory的過程

查看源碼可以得到創建sqlSessionFactory的時序圖如下:
在這裏插入圖片描述
步驟解析:

  1. 首先創建SqlSessionFactoryBuilder對象
  2. 然後調用SqlSessionFactoryBuilder對象的build方法,inputStream指的是全局配置文件的輸入流
  3. 創建解析器parser(類型是XmlConfigBuilder)
  4. 解析全局配置文件中每一個標籤並且將標籤對應的相關配置信息保存在一個Configuration對象中
  5. 解析mapper標籤指定的mapper.xml。解析mapper.xml使用的是也是一個解析器(只不過類型是XmlMapperBuilder)。和解析全局配置文件類似,在解析mapper.xml的時候也是解析mapper.xml配置文件中的每一個標籤並且將相關配置信息保存在了同一個Configuration對象中。
    注意:在解析mapper的增刪改查標籤的時候,使用的是類型爲XmlStatementBuilder的paser。這個paser將增刪改查標籤的所有詳細信息全部提取出來,封裝成了一個MappedStatement,一個MappedStatement就代表一個增刪改查標籤的詳細信息。
  6. 解析完mapper之後,將mapper的信息放入Configuration對象中,返回Configuration對象
  7. build中傳入Configuration對象
  8. build中傳入的Configuration對象被用來創建了一個DefaultSqlSessionFactory(圖片上寫的DefaultSqlSession是錯的)對象。
  9. 最終返回一個DefaultSqlSessionFactory。DefaultSqlSessionFactory裏面包含了Configuration對象,。在這裏插入圖片描述
    一個Configuration對象例子:保存了所有配置文件的詳細信息(全局配置文件、mapper.xml).
    在這裏插入圖片描述
    Configuration對象中包含了比較重要的MappedStatement對象以及knowMappers(保存了每一個接口對應的代理對象的工廠MapperProxyFactory)。
    在這裏插入圖片描述
    一個MappedStatement對象例子:裏面包含了sql id以及sql語句等重要信息。
    在這裏插入圖片描述

總結:創建sqlSessionFactory所做的工作就是,將所有配置文件的信息解析並且保存在Configuration對象中,返回包含了Configuration對象的DefaultSqlSessionFactory對象。

2.2 獲取sqlSession對象的過程

查看源碼可以得到創建sqlSession對象的時序圖如下:
在這裏插入圖片描述
步驟解析:

  1. 首先調用DefaultSqlSessionFactory的openSession()方法
  2. OpenSession()裏面其實是調用的openSessionFromSource()方法
  3. 獲取Configuration裏面的一些內容,創建一個事務(Transaction)
  4. 創建一個Executor對象
  5. 根據Executor在全局配置中的類型(目前類型有3種,simple,batch,reuse),創建出SimpleExecutor、ReuseExecutor或者BatchExecutor。Executor是一個接口,裏面規定了一些增刪改查的接口方法。
  6. 如果全局配置中開啓了二級緩存,那麼會使用CacheExecutor對Executor進行包裝,創建一個CacheExecutor對象。包裝的好處是在查詢的時候會先查緩存。
  7. 調用interceptorChain.pluginAll(executor),pluginAll方法會拿到每一個攔截器的plugin方法包裝一下executor。(這一步很重要,和插件有關)。
  8. 創建DefaultSqlSession對象,這個對象包含Configuration對象以及Executor對象
  9. 返回DefaultSqlSession對象

這一步返回SqlSession的實現類DefaultSqlSession對象,它裏面包含了Configuration對象和Executor對象。四大對象的Executor對象會在這一步被創建。

2.3 獲取Mapper的代理對象(MapperProxy)的過程

查看源碼可以得到獲取Mapper的代理對象的時序圖如下:
在這裏插入圖片描述
步驟解析:

  1. 調用DefaultSqlSession對象的getMapper(type)方法。(type是Mapper接口的class,例如XXXMapper.class)
  2. 這個方法裏面又會調用的是Configuration對象的getMapper(type)方法
  3. Configuration對象的getMapper(type)方法會調用MapperRegistry的getMapper(type)方法。MapperRegistry是Configuration對象的一個屬性。
  4. 根據接口類型(xxxMappee.class)獲取MapperProxyFactory對象
  5. 調用MapperProxyFactory的newInstance方法
  6. newInstance第一步會創建MapperProxy對象(它是一個InvocationHandler對象)。InvocationHandler是JDK裏面用於創建動態代理的接口。(動態代理的相關知識需要理解)
  7. 創建MapperProxy的代理對象
  8. 返回MapperProxy代理對象

一個MapperProxy代理對象例子:裏面包含了SqlSession對象。
在這裏插入圖片描述
getMapper:使用MapperProxyFactory對象創建一個MapperProxy代理對象,代理對象裏面包含了DefaultSqlSession,DefaultSqlSession裏面又包含了Executor,這樣代理對象就能使用Executor執行增刪改查了。

2.4 執行增刪改查方法的過程

查看源碼可以得到執行查詢方法的時序圖如下:
在這裏插入圖片描述

步驟解析:

  1. 調用代理對象的invoke方法,因爲MapperProxy對象是一個代理對象(類型是InvocationHandler),執行真正的目標方法之前,會執行InvocationHandler的invoke方法。這裏的知識點和Java的動態代理有關。
  2. invoke方法會調用MapperMethod的execute方法,execute方法會判斷當前要執行的sql語句的類型(增刪改查,當前是查詢類型)。
  3. 包裝參數爲一個map或者直接返回。如果參數是單個,直接返回,如果參數是多個,返回一個map
  4. 例子是返回一個對象,會調用DefaultSqlsession的selectOne方法。查詢多個會調用executorForMany。
  5. selectOne方法會調用DefaultSqlsession的selectList方法
  6. 獲取MappedStatement對象,裏面包含了sql id以及sql語句等重要信息。
  7. 調用CacheingExecutor對象的query(ms, xx, xx)方法,這裏會查詢二級緩存
  8. 獲取BoundSql對象,裏面包含sql語句的詳細信息。sql語句是什麼、sql語句的參數是什麼等詳細信息
  9. 調用真正的simpleExecutor的query方法進行查詢
  10. 查看本地緩存(一級緩存) 是否有數據,沒有就調用queryFromDataBase方法,查出以後結果也會保存在本地緩存中
  11. 調用BaseExecutor的doQuery方法
  12. 創建四大對象之一的StatementHandler對象,默認的類型是PrepareStatementHandler,可以通過標籤的StatementType進行指定,不指定就是Prepare類型。StatementHandler被用於創建Statement對象。
  13. 創建出來的StatementHandler會使用攔截器鏈進行進一步的處理
  14. 創建四大對象之一的ParameterHandler,創建出來的ParameterHandler也會使用攔截器鏈進行進一步的處理
  15. 創建四大對象之一的ResultSetHandler,創建出來的ResultSetHandler也會使用攔截器鏈進行進一步的處理
  16. 預編譯sql,產生PrepareStatement對象
  17. 調用ParameterHandler設置參數
  18. ParameterHandler設置參數的時候是調用的TypeHandler給sql預編譯設置參數
  19. 查出數據之後,使用ResultHandler處理結果,裏面也是使用TypeHandler獲取value值
  20. 後續的關閉連接等操作
  21. 返回結果

一個BoundSql對象的例子:
在這裏插入圖片描述
增刪改查流程總結:
在這裏插入圖片描述

  1. 使用代理對象進行增刪改查操作
  2. 代理對象在進行增刪改查操作的時候實際上使用的DefaultSqlSession
  3. DefaultSqlSession執行增刪改查又使用的是Executor
  4. Exexutor對象在執行增刪改查的時候,會創建StatementHandler,StatementHandler用於處理sql語句預編譯,設置參數等相關工作
  5. StatementHandler在候會藉助ParameterHandler來設置sql語句的預編譯參數
  6. 當增刪改查執行完成之後,StatementHandler會藉助ResultsetHandler來處理結果集
  7. ParameterHandler和ResultsetHandler都會藉助TypeHandler來進行數據庫類型和Java Bean類型的轉換
  8. 所有的底層操作都是藉助於原生的JDBC

需要記住Executor、ParameterHandler、ResultsetHandler、TypeHandler四大對象的作用。Executor用於執行增刪改查操作;Executor執行增刪改查是藉助於StatementHandler;StatementHandler用ParameterHandler設置參數;使用ResultsetHandler處理結果。整個過程中,設計到數據庫類型和Java Bean類型的轉換又使用的是TypeHandler。

3. 總結

整個流程的總結:

  1. 根據配置文件(全局、sql映射文件(mapper.xml))創建出Configuration對象
  2. 創建一個DefaultSqlSession對象,它裏面包含Configuration對象和Executor對象(根據全局配置文件中的defaultExecutorType進行創建)
  3. 通過DefaultSqlSession的getMapper()方法拿到Mapper接口的代理對象MapperProxy;MapProxy對象裏面有DefaultSqlSession對象
  4. 使用代理對象執行增刪改查方法。
    1)首先會調用DefaultSqlSession的增刪改查方法,DefaultSqlSession的增刪改查實際上是調用的Executor的增刪改查;
    2)Executor的增刪改查創建StatementHandler對象,同時也會創建出ParameterHandler和ResultsetHandler對象;
    3)使用StatementHandler設置sql參數值已經預編譯sql語句,藉助的是ParameterHandler;
    4)調用StatementHandler的增刪改查方法
    5)使用ResultsetHandler處理結果

注意:四大對象在創建的時候,都會調用攔截器鏈的pluginAll方法。Executor對象在創建SqlSession對象的時候創建;其他的三大對象在執行增刪改查方法的時候創建。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章