摘自:https://www.cnblogs.com/qm-article/p/11785350.html
一、mybatis的插件介紹
關於mybatis的插件,我想大家也都用過,就比如最常用的逆向工程,根據表結構生成model,dao,xml文件,還有分頁插件,那這些插件的工作原理是怎么樣的呢,就比如分頁插件,它為什么能改變我們在xml文件中寫的sql語句,本文將帶大家一起來了解mybatis的插件機制。(由於本人也是在不斷學習,文中難免有錯誤或不足之處,還望指正,本文基於mybatis3.3.0版本),下面將圍繞這幾個方面
1、插件入口,即怎么把插件注入到mybatis代碼里面
2、插件能攔截哪些類或哪些方法
3、舉例簡易分表插件原理
二、插件入口
在先了解前,我們來一段自定義mybatis插件的代碼
1 import org.apache.ibatis.executor.statement.StatementHandler;
2 import org.apache.ibatis.plugin.*;
3
4 import java.sql.Connection;
5 import java.util.Properties;
6
7 @Intercepts(
8 value = {
9 @Signature(
10 type = StatementHandler.class,
11 method = "prepare",
12 args = {Connection.class} // 不同版本的prepare方法參數不一樣,高版本的還有一個Integer參數
13 )
14 }
15 )
16 public class PluginDemo implements Interceptor {
17 @Override
18 public Object intercept(Invocation invocation) throws Throwable {
19 return invocation.proceed();
20 }
21
22 @Override
23 public Object plugin(Object target) {
24 return Plugin.wrap(target,this);
25 }
26
27 @Override
28 public void setProperties(Properties properties) {
29
30 }
要自定義mybatis插件,必須得實現Interceptor接口,這個接口有三個抽象方法
1、intercept,這個方法是mybatis的核心方法,要實現自定義邏輯,基本都是改造這個方法,其中invocation參數可以通過反射要獲取原始方法和對應參數信息
2、plugin,它的作用是用來生成一個攔截對方,也就是代理對象,使得被代理的對象一定會經過intercept方法,通常都會使用mybatis提供的工具類Plugin來獲取代理對象,如果有自己獨特需求,可以自定義
3、setProperties,這個方法就是用來設置插件的一些屬性
其中@intercepts注解就是用來標明攔截哪些類,哪些方法。
當我們脫離spring容器來使用mybatis的時候,我們通常是這樣寫代碼的

而在mybatisConfig.xml文件中,我們有配置數據庫連接信息,插件,別名,mapper文件映射地址等信息,(溫馨提示,這些配置有一定順序,如果不按照順序配置,則mybatis解析時會拋出異常,詳細配置信息可以參考mbatis的dtd文件配置)既然在配置文件中配置了,那肯定也會被解析掉,如下圖,在XmlConfigBuild中進行解析

解析后,插件信息會被存儲到configuation中InterceptorChain集合里面,這個configuation在mybatis運行周期是一個單例的,它負責存儲所有的配置信息

最終,mybatis的插件信息完整的注入到了configuation里面。
二、mybaits插件能攔截哪些類或哪些方法
在正常開發流程中,我們基本都是通過先獲取sqlSession,如果不采用自定義配置,在默認的sqlsession實現就是defaultSqlSession,在該類的一個方法里就隱含了融合插件,

其中會獲取到executor,這個類是通過configuation獲取的,

最終融合插件的方法就是圖中紅框的代碼。它會為原始的executor類生成代理類。從而你在執行executor類的一些方法時,比如query,update方法,會先生成對應的代理對象,myabtis采用的是jdk的動態代理,代理后,你執行executor類的query,update方法時會自動轉接到你自定義的插件intercept方法里面,也可以理解為覆蓋原來的方法。
通過這,我們大概也可以猜測的出,mybatis插件要攔截的類,很大原因在configuation類中有生成,也不帶大家繞彎子了,來看截圖
攔截statementHandler接口的實現類,它通過routingStatementHandler來實現路由,最終定向到prepareStatementHandler類

攔截parameterHandler。這個接口是用來封裝參數用的,也就是最終將參數設置到sql里面

攔截ResultSetHandler接口,它主要用於處理sql運行返回的結果

總結,mybatis可以攔截的類分四大類
一、executor,executor類可以說是執行sql的全過程,如組裝參數,sql改造,結果處理,比較廣泛,但實際用的不多
二、StatementHandler,這個是執行sql的過程,可以獲取到待執行的sql,可用來改造sql,如分頁,分表,最常攔截的類
三、paremeterHandler,這個用來攔截sql的參數,可以自定義參數組裝規則
四、resultHandler,這個用來處理結果
三、簡易版分表插件
1 import org.apache.ibatis.executor.statement.StatementHandler;
2 import org.apache.ibatis.mapping.BoundSql;
3 import org.apache.ibatis.plugin.*;
4 import org.apache.ibatis.reflection.MetaObject;
5 import org.apache.ibatis.reflection.SystemMetaObject;
6
7 import java.sql.Connection;
8 import java.util.Properties;
9
10 @Intercepts(
11 value = {
12 @Signature(
13 type = StatementHandler.class,
14 method = "prepare",
15 args = {Connection.class}
16 )
17 }
18 )
19 public class PluginDemo implements Interceptor {
20 @Override
21 public Object intercept(Invocation invocation) throws Throwable {
22
23 /**
24 * 獲取被攔截的目前類,在這里是攔截了statementHandler,所有目前類也就是它
25 * 通過這個類我們可以拿到待執行的sql語句,通常使用mataObject工具類來獲取
26 * 關於這個工具類,大家可自行了解,個人認為這個工具類很強大
27 */
28 StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
29 MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
30 /**
31 * 先解釋下為什么寫成delegate.boundSql就可以拿到boundSql類
32 * 從前面也可以得知,statementHandler的默認實現是routingStatementHandler。
33 * 這個類有一個屬性statementHandler,屬性名就叫delegate,而這個屬性的默認實現又是preparedStatementHandler
34 * 后面這個類又有屬性boundSql,所以,最終形成的寫法就是delegate.boundSql。
35 * 所以這也體現了MetaObject工具類的強大,可以通過實例傳參,就可以根據屬性名獲取對應屬性值
36 */
37 BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
38
39 // 待執行的sql,在這里也就是預編譯后的sql,即參數位都是?號
40 String sql = boundSql.getSql();
41 /**
42 * 既然拿到了預編譯后的sql,那就可以按照你自己的想法為所欲為,如分頁,按年分表等等
43 * 分表的話,個人推薦druid的sql解析器,我認為還是不錯的,大家可以自行了解
44 * 最后改造完sql,別忘了把它設置回去
45 * metaObject.setValue("delegate.boundSql.sql",sql);
46 * invocation.proceed,即原始方法的執行
47 * 注意點就是,因為mybatis插件采用的是代理模式,所以如果存在多個插件,會形成多個代理
48 * 你如果要拿到最原始的對象,還得進一步進行分解
49 * 如:while(metaObject.getValue(""h) != null){
50 * Object obj = metaObject.getValue("h");
51 * ....
52 * }
53 */
54 return invocation.proceed();
55 }
56
57 @Override
58 public Object plugin(Object target) {
59 return Plugin.wrap(target,this);
60 }
61
62 @Override
63 public void setProperties(Properties properties) {
64
65 }
66 }
-----------------------------------------------------------------------------------------------------------------------------分界線-------------------------------------------------------------------------------------
以上就是全部內容

