mybatis的插件機制

1、mybatis的插件介紹

關於mybatis的插件,我想你們也都用過,就好比最經常使用的逆向工程,根據表結構生成model,dao,xml文件,還有分頁插件,那這些插件的工做原理是怎麼樣的呢,就好比分頁插件,它爲何能改變咱們在xml文件中寫的sql語句,本文將帶你們一塊兒來了解mybatis的插件機制。(因爲本人也是在不斷學習,文中不免有錯誤或不足之處,還望指正,本文基於mybatis3.3.0版本),下面將圍繞這幾個方面java

一、插件入口,即怎麼把插件注入到mybatis代碼裏面spring

二、插件能攔截哪些類或哪些方法sql

三、舉例簡易分表插件原理數據庫

2、插件入口

在先了解前,咱們來一段自定義mybatis插件的代碼apache

 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接口,這個接口有三個抽象方法數組

一、intercept,這個方法是mybatis的核心方法,要實現自定義邏輯,基本都是改造這個方法,其中invocation參數能夠經過反射要獲取原始方法和對應參數信息session

二、plugin,它的做用是用來生成一個攔截對方,也就是代理對象,使得被代理的對象必定會通過intercept方法,一般都會使用mybatis提供的工具類Plugin來獲取代理對象,若是有本身獨特需求,能夠自定義mybatis

三、setProperties,這個方法就是用來設置插件的一些屬性app

其中@intercepts註解就是用來標明攔截哪些類,哪些方法。ide

當咱們脫離spring容器來使用mybatis的時候,咱們一般是這樣寫代碼的

 

 

 而在mybatisConfig.xml文件中,咱們有配置數據庫鏈接信息,插件,別名,mapper文件映射地址等信息,(舒適提示,這些配置有必定順序,若是不按照順序配置,則mybatis解析時會拋出異常,詳細配置信息能夠參考mbatis的dtd文件配置)既然在配置文件中配置了,那確定也會被解析掉,以下圖,在XmlConfigBuild中進行解析

 

解析後,插件信息會被存儲到configuation中InterceptorChain集合裏面,這個configuation在mybatis運行週期是一個單例的,它負責存儲全部的配置信息

 

 

 

 

 最終,mybatis的插件信息完整的注入到了configuation裏面。

2、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能夠攔截的類分四大類

1、executor,executor類能夠說是執行sql的全過程,如組裝參數,sql改造,結果處理,比較普遍,但實際用的很少

2、StatementHandler,這個是執行sql的過程,能夠獲取到待執行的sql,可用來改造sql,如分頁,分表,最常攔截的類

3、paremeterHandler,這個用來攔截sql的參數,能夠自定義參數組裝規則

4、resultHandler,這個用來處理結果

3、簡易版分表插件

 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 }

 

 -----------------------------------------------------------------------------------------------------------------------------分界線-------------------------------------------------------------------------------------

以上就是所有內容

相關文章
相關標籤/搜索