MyBatis(十一):MyBatis架構流程淺析

  1. 架構分層

    我們將MyBatis架構分為三層,分別為接口層、數據處理層和框架支撐層

    • 接口層:提供外部接口調用的API,使用端通過這些API來操作數據庫,接口層收到請求後會調用數據處理層完成具體的數據處理

      使用端可以通過兩種方式調用接口層:MyBatis提供的API、Mapper動態代理

    • 數據處理層:負責具體的SQL查找、解析、執行與執行結果映射處理,主要負責根據具體的請求完成數據庫操作

    • 框架支撐層:負責基礎的功能支撐,主要功能有連接管理、事務管理、配置加載和緩存管理

  2. 主要組件及關係

    組件 功能描述
    Configuration 接收使用端配置的MyBatis XML核心配置文件
    MappedStatement 接收使用端配置的XxMapper.xml文件
    SqlSession 負責與數據庫交互的會話,主要封裝了代理對象、增刪改查、獲取數據庫連接
    Executor 執行器,MyBatis的核心調度器,主要負責動態語句的生成、事務管理及緩存操作
    StatementHandler 封裝了諸多類,調度參數映射、SQL執行與結果集映射,主要負責 操作JDBC Statement對象執行SQL,
    ParameterHandler 負責將使用端傳遞的參數轉換成JDBC Statement所需要的參數
    ResultSetHandler 負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合
    TypeHandler 主要負責JDBC對象與Java對象之間進行相互轉換
    BoundSql 處理SQL中的#{} ${},進行替換處理,轉換為數據庫可執行的SQL
    RowBounds MyBatis提供的邏輯分頁類,從結果集中進行過濾分頁數據
  3. 執行流程

    1、讀取Mybatis主配置文件信息

    2、獲取SqlSessionFactory

    ​ a、使用XMLMappperBuilder解析Mybatis配置文件,封裝成Environment對象,再把Environment對象設置給Configuration對象;

    ​ b.、調用ConfigurationElement函數,最終執行addMappedStatement方法,將mapper配置文件中的每一條SQL語句封裝成mappedStatement對象,作為value保存在HashMap集合中;

    ​ c.、進入addLoaderResource方法,使用HashSet集合存放mybatis的mapper.xml 映射文件路徑地址;

    ​ d.、進入bindMapperForNamespace()方法,通過namespace使用Java反射機制找到mapper接口,再調用addMapper()方法,判斷是否是接口類型,是否註冊過(註冊過則拋出異常)其中mapperRegistry通過HashMap保存mapper接口,【key:接口;value:MapperProxyFactory】

    3、獲取SqlSession對象

    ​ a、進入openSession()方法,執行newExecutor()方法創建執行器;

    ​ b、先創建 SimpleExecutor簡單執行器,再判斷是否開啟了二級緩存,默認是開啟的(使用時需要配置條件),就會去創建CacheExecutor緩存執行器

    ​ c、執行interceptorChain.pluginAll()方法,責任鏈設計模式,底層使用動態代理技術,使開發者可以自定義插件開發,只需要實現Interceptor接口,並指定想要攔截的方法簽名即可,最後返回執行器;

    4、操作mapper接口

    ​ a、調用getMapper()方法,最終執行mapperProxyFactory.newInstance(sqlSession)方法創建代理類MapperProxy;

    ​ b、當我們調用mapper,getUser()方法的時候,就會去執行MapperProxy代理類的invoke()方法;

    ​ c、判斷mapper接口是否有實現類,顯然我們沒有實現類,則調用cacheMapperMethod()方法去緩存中獲取要代理的方法method;

    ​ d、進入cacheMapperMethod()方法先去查找緩存中有沒有,沒有的話將mapper配置文件中配置的SQL語句和對應的mapper接口方法進行關聯並放入map緩存中,後期直接走緩存了,最後執行execute()方法;

    ​ e、執行execute()方法,最終調用selectOne()方法;

    ​ f、進入selectOne()方法,底層還是查詢所有的,但是取第一個,查詢多個的話會拋出異常;

    ​ g、進入selectList()方法,調用getMapperStatement()方法獲取對應的SQL語句;

    ​ h、執行query()方法進行查詢,判斷如果開啟了二級緩存並且配置了二級緩存存儲介質(Redis,EhCache..)則先走二級緩存中查詢數據,第一次查詢是沒有緩存數據的,則刷新緩存配置,清除緩存。

    ​ i、二級緩存(sessionFactory)中沒有查詢到數據,就回去執行BaseExecutor去查詢 HashMap一級緩存中(sqlSession)是否有緩存數據,一級緩存(PerpetualCache)存放在內存中的,同理也是沒有的,最後查詢數據庫DB

    ​ j、將從數據庫查詢出來的數據緩存到一級緩存中,再把一級緩存中的數據同步到二級緩存,添加到二級緩存之前先添加到getTransactionalCache的entritiesToAddOnCommit的map集合中臨時緩存起來;

    ​ k、調用executor.close()方法循環迭代TransactionCache,最後將臨時map緩存數據提交到二級緩存中,如果事務回滾,則會將緩存數據清除掉

    ​ l、再次查詢的話,就有緩存了,會直接查詢到緩存數據返回,不會去查詢數據庫DB
    [執行流程]: “//www.douban.com/note/749318390/

Tags: