Mybatis源碼分析

這篇文章我們來深入閱讀下Mybatis的源碼,希望以後可以對底層框架不那麼畏懼,學習框架設計中好的思想;

架構原理


架構圖

架構圖

架構流程圖

架構流程圖

上面這兩幅圖來源於網絡,不過畫的很好,基本說明了Mybatis的架構流程。

說明:

  1. Mybatis配置文件

    • SqlMapConfig.xml,此文件作爲mybatis的全局配置文件,配置了mybatis的運行環境等信息。
    • Mapper.xml,此文件作爲mybatis的sql映射文件,文件中配置了操作數據庫的sql語句。此文件需要在 SqlMapConfig.xml中加載。
  2. SqlSessionFactory

    • 通過mybatis環境等配置信息構造SqlSessionFactory,即會話工廠。
  3. SqlSession

    • 通過會話工廠創建sqlSession即會話,程序員通過sqlsession會話接口對數據庫進行增刪改查操作。
  4. Executor執行器

    • mybatis底層自定義了Executor執行器接口來具體操作數據庫,Executor接口有兩個實現,一個是基本執行器(默認)、一個是緩存執行器,sqlsession底層是通過executor接口操作數據庫的。
  5. MappedStatement

    • 它也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個selectinsertupdatedelete標籤對應一個Mapped Statement對象,selectinsertupdatedelete標籤的id即是Mapped statement的id。
    • Mapped Statement對sql執行輸入參數進行定義,包括HashMap、基本類型、pojo,Executor通過MappedStatement在執行sql前將輸入的java對象映射至sql中,輸入參數映射就是jdbc編程中對preparedStatement設置參數。
    • Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本類型、pojo,Executor通過MappedStatement在執行sql後將輸出結果映射至java對象中,輸出結果映射過程相當於jdbc編程中對結果的解析處理過程。

調用流程圖

調用流程圖

Executor

MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護

StatementHandler

封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設置參數、將Statement結果集轉換成List集合。

ParameterHandler

負責對用戶傳遞的參數轉換成JDBC Statement 所需要的參數

ResultSetHandler

負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合

TypeHandler

負責java數據類型和jdbc數據類型之間的映射和轉換

SqlSource

負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,並返回BoundSql表示動態生成的SQL語句以及相應的參數信息

源碼解析


加載全局配置文件

  • 找入口:SqlSessionFactoryBuilder#build方法
SqlSessionFactoryBuilder#build 構建SqlSessionFactory
    XMLConfigBuilder#parse 全局配置文件解析,封裝成Configuration對象
        #parseConfiguration 從根路徑開始解析,加載的信息設置到Configuration對象中
            #mapperElement 解析mapper映射文件
                XMLMapperBuilder#parse 具體解析mapper映射文件
                    SqlSessionFactoryBuilder#build:創建SqlSessionFactory接口的默認實現類
  • 總結
1.SqlSessionFactoryBuilder創建SqlsessionFactory時,需要傳入一個Configuration對象。 
2.XMLConfigBuilder對象會去實例化Configuration。 
3.XMLConfigBuilder對象會去初始化Configuration對象。 
    通過XPathParser去解析全局配置文件,形成Document對象 
    通過XPathParser去獲取指定節點的XNode對象。 
    解析Xnode對象的信息,然後封裝到Configuration對象中
  • 相關類和接口
|--SqlSessionFactoryBuilder 
|--XMLConfigBuilder 
|--XPathParser 
|--Configuration

加載映射配置文件

  • 找入口:XMLConfigBuilder#mapperElement方法
XMLConfigBuilder#mapperElement:解析全局配置文件中的<mappers>標籤 
    |--XMLMapperBuilder#構造方法:專門用來解析映射文件的 
        |--XPathParser#構造方法: 
            |--XPathParser#createDocument():創建Mapper映射文件對應的Document對象 
            |--MapperBuilderAssistant#構造方法:用於構建MappedStatement對象的 
        |--XMLMapperBuilder#parse(): 
            |--XMLMapperBuilder#configurationElement:專門用來解析mapper映射文件 
                |--XMLMapperBuilder#buildStatementFromContext:用來創建MappedStatement對象的 
                    |--XMLMapperBuilder#buildStatementFromContext 
                        |--XMLStatementBuilder#構造方法:專門用來解析MappedStatement 
                        |--XMLStatementBuilder#parseStatementNode: 
                            |--MapperBuilderAssistant#addMappedStatement:創建 MappedStatement對象 
                                |--MappedStatement.Builder#構造方法 
                                |--MappedStatement.Builder#build方法:創建MappedStatement對象,並存儲 到Configuration對象中
  • 相關接口和類
|--XMLConfigBuilder 
|--XMLMapperBuilder 
|--XPathParser 
|--MapperBuilderAssistant 
|--XMLStatementBuilder 
|--MappedStatement

SqlSource創建流程

  • 找入口:XMLLanguageDriver#createSqlSource
XMLLanguageDriver#createSqlSource 創建SqlSource,解析SQL,封裝SQL語句(出參數綁定)和入參信息

​    XMLScriptBuilder 構造函數:初始化動態SQL中的節點處理器集合
​        XMLScriptBuilder#parseScriptNode 
​            #parseDynamicTags 解析select\insert\ update\delete標籤中的SQL語句,最終將解析到的SqlNode封裝到MixedSqlNode中的List集合中
​            DynamicSqlSource 構造方法:如果SQL中包含${}和動態SQL語句,則將SqlNode封裝到DynamicSqlSource
​            RawSqlSource 構造方法:如果SQL中包含#{},則將SqlNode封裝到RawSqlSource中,並指定parameterType
​                SqlSourceBuilder#parse
​                    ParameterMappingTokenHandler 構造方法
​                        GenericTokenParser#構造方法,指定待分析的openToken和closeToken並指定處理器
​                            GenericTokenParser#parse 解析#{}
​                                ParameterMappingTokenHandler#handleToken  處理token(#{}/${})
​                                    #buildParameterMapping 創建ParameterMapping對象
​                                StaticSqlSource 構造方法,將解析之後的sql信息,封裝到StaticSqlSource 對象
  • 相關類和接口
|--XMLLanguageDriver 
|--XMLScriptBuilder 
|--SqlSource 
|--SqlSourceBuilder

創建Mapper代理對象

  • 找入口:DefaultSqlSession#getMapper
|--DefaultSqlSession#getMapper:獲取Mapper代理對象 
​    |--Configuration#getMapper:獲取Mapper代理對象 
​        |--MapperRegistry#getMapper:通過代理對象工廠,獲取代理對象 
​            |--MapperProxyFactory#newInstance:調用JDK的動態代理方式,創建Mapper代理

SqlSession執行主流程

  • 找入口:DefaultSqlSession#selectList()
DefaultSqlSession#selectList
​    CachingExecutor#query
​        BaseExecutor#query 委託給BaseExecutor執行
​            #queryFromDatabase
​            SimpleExecutor#doQuery  執行查詢
​                Configuration#newStatementHandler  創建路由功能的StatementHandler,根據MappedStatement中的StatementType
​            SimpleExecutor#prepareStatement  設置PreapreStatement 的參數
​            BaseExecutor#getConnection 獲取數據庫連接
​                BaseStatementHandler#prepare 創建Statement PrepareStatement、Statement、CallableStatement)
​                PreparedStatementHandler#parameterize 設置參數
​                PreparedStatementHandler#query 執行SQL語句(已經設置過參數),並且映射結果集
​                    com.mysql.jdbc.PreparedStatement#execute 調用JDBC的api執行Statement
​                        DefaultResultSetHandler#handleResultSets  處理結果集
  • 相關接口和類
|--DefaultSqlSession 
|--Executor 
    |--CachingExecutor 
    |--BaseExecutor 
    |--SimpleExecutor 
|--StatementHandler 
    |--RoutingStatementHandler 
    |--PreparedStatementHandler 
|--ResultSetHandler 
    |--DefaultResultSetHandler 

BoundSql獲取流程

  • 找入口:MappedStatement#getBoundSql方法
MappedStatement#getBoundSql
​    DynamicSqlSource#getBoundSql 
​        SqlSourceBuilder#parse 執行解析:將帶有#{}的SQL語句進行解析,然後封裝到StaticSqlSource中
​            GenericTokenParser  #構造方法,指定待分析的openToken和closeToken,並指定處理器
​                GenericTokenParser#parse 解析SQL語句,處理openToken和closeToken中的內容 
​                    ParameterMappingTokenHandler#handleToken 處理token(#{}/${})  
​                        #buildParameterMapping  創建 ParameterMapping對象
​                            StaticSqlSource#構造方法 : 將解析之後的SQL信息,封裝到StaticSqlSource
|--RawSqlSource#getBoundSql 
​    |--StaticSqlSource#getBoundSql 
​        |--BoundSql#構造方法:將解析後的sql信息、參數映射信息、入參對象組合到BoundSql對象中 

參數映射流程

  • 找入口: 其實就是SqlSession執行流程中的 PreparedStatementHandler#parameterize

|--PreparedStatementHandler#parameterize:設置PreparedStatement的參數 
​    |--DefaultParameterHandler#setParameters:設置參數 
​        |--BaseTypeHandler#setParameter: 
​            |--xxxTypeHandler#setNonNullParameter:調用PreparedStatement的setxxx方法

處理結果集

  • 找入口:其實就是SqlSession執行流程中的 DefaultResultSetHandler#handleResultSets
|--DefaultResultSetHandler#handleResultSets 
​    |--DefaultResultSetHandler#handleResultSet 
​        |--DefaultResultSetHandler#handleRowValues 
​            |--DefaultResultSetHandler#handleRowValuesForSimpleResultMap 
​                |--DefaultResultSetHandler#getRowValue 
​                    |--DefaultResultSetHandler#createResultObject:創建映射結果對象 
​                    |--DefaultResultSetHandler#applyAutomaticMappings 
​                    |--DefaultResultSetHandler#applyPropertyMappings 

基本上Mybatis的流程就是這樣了,其中還有很多實現細節暫時看不太懂。 我認爲學習框架源碼分爲兩步:

  1. 抓住主線,掌握框架的原理和流程;
  2. 理解了處理思路之後,再去理解面向對象思想和設計模式的用法;

目前第一步尚有問題,需要多走幾遍源碼,加深下理解,一起加油~~

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