mybatis的插件原理可參考博文,http://blog.csdn.net/hupanfeng/article/details/9247379,本文可作爲補充。
1、mybatis的自定義插件,需要實現Interceptor接口,裏面有三個接口:
Object intercept(Invocation var1) throws Throwable;
Object plugin(Object var1);
void setProperties(Properties var1);
調用順序分別是:setProperties(xxx) => plugin(xx), => intercept(xxx);第一個是設置參數,第二個是生成動態代理(爲考慮性能,一般會判斷是否當前信息是否是當前插件需要攔截的動作),第三個是執行(額,也就是自定義插件的具體內容需要寫在這裏)。
2、InterceptorChain裏保存了所有的攔截器,他有一個pluginAll方法用來創建已註冊的插件的動態代理,
該方法的其中一條調用路徑(執行一條SQL時,即使是執行一條SQL這樣簡單的任務,pluginAll也可能會被調用很多次)如下圖所示:
而InterceptorChain裏註冊的插件interceptors是在解析XML配置時注入的,
3、Object plugin(Object var1);
假設原始對象的類型是T,被攔截的方法是m.
該方法返回插件的代理對象(是JVM臨時生成的一個繼承T的類(如,$Proxy35),同時傳入一個InvocationHandler對象(ProxyWrapper)或者原始對象,該類有三個成員,target、interceptor和signatureMap。
target是下一層插件的代理對象或原始對象(你看,這就構成了代理鏈);
interceptor是當前層次的插件;
signatureMap保存了哪些類(原始對象對應的類)的哪些方法被攔截
4、Object intercept(Invocation var1);
參數傳入的是代理鏈接上的下一個插件的相關參數,Invocation同樣包含三個成員,target、method和args。
target是下一級插件的代理對象或原始對象,method被攔截的方法,args對應參數。
末尾有一句
如果target是插件的話,觸發插件的執行(即經歷代理對象的invoke(xx)=> ProxyWrapper的invoke(xxx)=>插件的intercept(xxx)),如此循環;
如果target是目標類的話,那麼調用被攔截的方法。
5、最外一層的代理對象的invoke方法何時被調用呢?
最外層的代理對象,因爲繼承了T,所以可以顯式調用方法m,從而轉到代理對象的invoke方法,進而轉到ProxyWrapper的invoke方法裏.
例如:
當handler是個代理對象時,handler.prepare(xxx)的調用會轉成代理對象(如類名爲”$Proxy35”的對象)的invoke方法的調用,再轉到ProxyWrapper的invoke方法裏,進而轉到插件的intercept(xx)裏面,你看進入4的循環了。
6、ProxyWrapper的invoke方法
這裏,如果被調用的方法是被攔截的方法,那麼調用插件的intercept(xxx)方法,並結束;否則呢,調用原始對象的被攔截方法。