Mybatis采取責(zé)任鏈模式,通過動態(tài)代理組織多個攔截器(插件),通過這些攔截器可以改變Mybatis的默許行動(諸如SQL重寫之類的),由于插件會深入到Mybatis的核心,因此在編寫自己的插件前最好了解下它的原理,以便寫出安全高效的插件。
Mybatis支持對Executor、StatementHandler、PameterHandler和ResultSetHandler進(jìn)行攔截,也就是說會對這4種對象進(jìn)行代理。下面以Executor為例。Mybatis在創(chuàng)建Executor對象時會履行下面1行代碼:
InterceptorChain里保存了所有的攔截器,它在mybatis初始化的時候創(chuàng)建。上面這句代碼的含義是調(diào)用攔截器鏈里的每一個攔截器順次對executor進(jìn)行plugin(插入?)代碼以下:
下面以1個簡單的例子來看看這個plugin方法里到底產(chǎn)生了甚么。
每個攔截器都必須實現(xiàn)上面的3個方法,其中:
1) Object intercept(Invocation invocation)是實現(xiàn)攔截邏輯的地方,內(nèi)部要通過invocation.proceed()顯式地推動責(zé)任鏈前進(jìn),也就是調(diào)用下1個攔截器攔截目標(biāo)方法。
2) Object plugin(Object target) 就是用當(dāng)前這個攔截器生成對目標(biāo)target的代理,實際是通過Plugin.wrap(target,this) 來完成的,把目標(biāo)target和攔截器this傳給了包裝函數(shù)。
3) setProperties(Properties properties)用于設(shè)置額外的參數(shù),參數(shù)配置在攔截器的Properties節(jié)點里。
注解里描寫的是指定攔截方法的簽名 [type,method,args] (即對哪一種對象的哪一種方法進(jìn)行攔截),它在攔截前用于決斷。
從前面可以看出,每一個攔截器的plugin方法是通過調(diào)用Plugin.wrap方法來實現(xiàn)的。代碼以下:
這個Plugin類有3個屬性:
private Object target;//被代理的目標(biāo)類
private Interceptor interceptor;//對應(yīng)的攔截器
private Map
我們再次結(jié)合(Executor)interceptorChain.pluginAll(executor)這個語句來看,這個語句內(nèi)部對
executor履行了屢次plugin,第1次plugin后通過Plugin.wrap方法生成了第1個代理類,姑且就叫executorProxy1,這個代理類的target屬性是該executor對象。第2次plugin后通過Plugin.wrap方法生成了第2個代理類,姑且叫executorProxy2,這個代理類的target屬性是executorProxy1...這樣通過每一個代理類的target屬性就構(gòu)成了1個代理鏈(從最后1個executorProxyN往前查找,通過target屬性可以找到最原始的executor類)。
代理鏈生成后,對原始目標(biāo)的方法調(diào)用都轉(zhuǎn)移到代理者的invoke方法上來了。Plugin作為InvocationHandler的實現(xiàn)類,他的invoke方法是怎樣樣的呢?