源碼總結回顧
對象 | 相關對象 | 作用 |
---|---|---|
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插件到底可以幹些什麼
- 動態改變數據源。水平分表,通過註解的方式:可以通過invocation拿到方法的註解,在接口上添加註解,通過反射獲取接口註解,根據註解上配置的參數進行分表,修改原 SQL,例如 id 取模,按月分表
- 記錄日誌。
- 數據權限—對不同用戶查詢出來的數據有些篩選
- 脫敏也是可以的,數據加解密
- 對數據查詢的執行分析,比如執行時長的分析。
插件調用的流程圖
上一個簡單的sql執行時長分析插件
type: 表示攔截的類,這裏是StatementHandler的實現類
method:表示攔截的方法,這裏是攔截StatementHandler的query方法
args:表示方法參數(可以通過數組參數接收然後強轉)
看看pagehelper的
SpirngBoot註冊插件