Mybatis3源碼分析(4)插件分析

源碼總結回顧

對象 相關對象 作用
Configuration MapperRegistry TypeAliasRegistry TypeHandlerRegistry 包含了 MyBatis 的所有的配置信息
SqlSession SqlSessionFactory DefaultSqlSession 對操作數據庫的增刪改查的 API 進行了封裝,提供給應用層使用
Executor BaseExecutor SimpleExecutor BatchExecutor ReuseExecutor MyBatis 執行器,是 MyBatis 調度的核心,負責 SQL 語句的生成和查 詢緩存的維護
StatementHandler BaseStatementHandler SimpleStatementHandler PreparedStatementHandler CallableStatementHandler 封裝了 JDBC Statement 操作,負責對 JDBC statement 的操作,如設 置參數、將 Statement 結果集轉換成 List 集合
ParameterHandler DefaultParameterHandler 把用戶傳遞的參數轉換成 JDBC Statement 所需要的參數
ResultSetHandler DefaultResultSetHandler 把 JDBC 返回的 ResultSet 結果集對象轉換成 List 類型的集合
MapperProxy MapperProxyFactory 代理對象,用於代理 Mapper 接口方法
MappedStatement SqlSource BoundSql MappedStatement 維護了一條select|update|delete|insert節點 的封裝,包括了 SQL 信息、入參信息、出參信息

插件攔截的四大對象

對象 描述 可攔截的方法 方法作用
Executor 上層的對象,SQL 執行全過程,包括組裝參數,組裝結果集返回和執行 SQL 過程 update 執行 update、insert、delete 操作
query 執行 query 操作
flushStatements 在 commit 的時候自動調用,SimpleExecutor ReuseExecutor、BatchExecutor 處理不同
commit 提交事務
rollback 事務回滾
getTransaction 獲取事務
close 結束(關閉)事務
isClosed 判斷事務是否關閉
StatementHandler 上層的對象,SQL 執行全過程,包括組裝參數,組裝結果集返回和執行 SQL 過程 prepare (BaseSatementHandler)SQL 預編譯
parameterize 設置參數
batch 批處理
update 增刪改操作
query 查詢操作
ParameterHandler SQL 參數組裝的過程 getParameterObject 獲取參數
setParameters 設置參數
ResultSetHandler 結果的組裝 handleResultSets 處理結果集
handleOutputParameters 處理存儲過程出參

代理和攔截是怎麼實現的?

我們從之前的課程知道插件是通過攔截時通過jdk動態代理的。

四大對象什麼時候被代理,也就是:代理對象是什麼時候創建的?

我們還記得Executor上個文章裏面講到,實在openSession時創建的,StatementHandler是SimpleExecutor.doQuery()裏面創建的,裏面包含了處理參數的 ParameterHandler 和處理結果集的 ResultSetHandler 的創建,創建之後即調用InterceptorChain.pluginAll(),返回層層代理後的對象。

多個插件的情況下,代理能不能被代理?代理順序和調用順序的關係?

誰來創建代理對象?

我們可以看從pagehelper這個插件進入看看-首先我們先從pageHelper的註冊類進去

可以看到實現了interceptor這個類----這樣就形成了代理類
下面這個方法就是mybatis的操作創建插件的方法:這個之後會分析

被代理後,調用的是什麼方法?怎麼調用到原被代理對象的方法?

我們可以看到在intercept方法裏面拿到執行器是調用的gettarget這個方法,利用代理類調用的,我們進入invocation看看
可以看到可以通過target方法拿到被代理對象

因爲代理類是 Plugin,所以最後調用的是 Plugin 的 invoke()方法。它先調用了定義的攔截器的 intercept()方法。可以通過 invocation.proceed()調用到被代理對象被攔截的方法。

再來看看他是怎麼對我們的天王代理的

通過openSession 進去找到創建executor的時候 他調用了我們放在Configuration裏面的interceptorChain

可以看到調用了所有插件-拿到所有實現了Interceptor的攔截器,然後這裏就調用了實現Interceptor的類的plugin這個方法進行創建代理插件代理對象。這樣被代理後我們之後的操作都會走到代理類的invoke方法裏面。


這樣就會調用我們自定義的攔截器的intercept方法–之後裏面就是我們自定已的邏輯操作了。

實現Interceptor的三個方法含義

intercept():做我們想要做的事情 可以通過invocation 這個參數獲取被攔截對象方法參數或者執行代理方法 --調用proceed可以走到被攔截對象的被攔截方法,這樣就走完整個流程了。
plugin(): 創建代理對象,通過代理對象的invoke方法走到我們的插件
setProperties():這是我們在配置我們插件時可以提供很多屬性,比如分頁插件設置一些參數等等。

pageHelper插件案例分析


他是怎麼來改寫我們的sql的呢–我們首先來看看PageInterceptor裏面的自定義邏輯

根據方言進入不同的實現類


然後我們在看看分頁參數哪裏來的

可以看到這裏startPage調用時傳進來的,然後通過本地線程裏面拿到的(每個線程裏面拿到的都是屬於自己獨有的分頁數據)

這也就是寫了startPage那句話,就會修改我們的sql 分頁–這也就出現一個問題,在一個線程裏我們的查詢分頁就有不穩定性!

對象 作用
PageInterceptor 自定義攔截器
Page 包裝分頁參數
PageInfo 包裝結果
PageHelper 工具類

mybatis插件到底可以幹些什麼

  1. 動態改變數據源。水平分表,通過註解的方式:可以通過invocation拿到方法的註解,在接口上添加註解,通過反射獲取接口註解,根據註解上配置的參數進行分表,修改原 SQL,例如 id 取模,按月分表
  2. 記錄日誌。
  3. 數據權限—對不同用戶查詢出來的數據有些篩選
  4. 脫敏也是可以的,數據加解密
  5. 對數據查詢的執行分析,比如執行時長的分析。

插件調用的流程圖

上一個簡單的sql執行時長分析插件

type: 表示攔截的類,這裏是StatementHandler的實現類
method:表示攔截的方法,這裏是攔截StatementHandler的query方法
args:表示方法參數(可以通過數組參數接收然後強轉)

看看pagehelper的

SpirngBoot註冊插件

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