1.插件接口
在Mybatis中使用插件的時(shí)候,就必須實(shí)現(xiàn)Interceptor接口。
public interface Interceptor{
Objectintercept(Invocation invocation) throws Throwable;
Objectplugin (Object target);
voidsetPRoperties(Properties properties);
}
Intercept方法:插件的核心方法,它將直接覆蓋你所攔截對(duì)象原有的方法。參數(shù)Invocation對(duì)象,通過它可以反射調(diào)度原來(lái)對(duì)象的方法。
Plugin方法:target是攔截的對(duì)象,它的作用是給攔截對(duì)象生成一個(gè)代理對(duì)象,并返回它。為了方便Mybatis使用org.apache.ibatis.plugin.Plugin中的wrap靜態(tài)方法提供生成代理對(duì)象,我們往往使用plugin方法便可以生成一個(gè)代理對(duì)象。
setProperties方法:允許在plugin元素中配置所需要的參數(shù),方便在插件初始化的時(shí)候就被調(diào)用一次,然后把插件對(duì)象存存入到配置中,以便以后在取出。
2. 插件的初始化
插件的初始化是在Mybatis初始化的時(shí)候完成的,這點(diǎn)可以通過xmlConfigBuilder中的代碼可以知道。
publicvoid pluginElment(XNode parent) throws Exception{
if(parent!=null){
for(XNode child:parent.getChildren()){
Stringinterceptor=child.getStringAtrribute("interceptor");
Propertiesproperties=child.getChildrenasproperties();
Interceptor interceptorInstance=(Interceptor)resolveClass(interceptor).newInstance();
interceptorInstance.setProperties(properties);
Configuration.addInterceptor(interceptorInstance);
}
}
}
在解析配置文件的時(shí)候,在Mybatis的上下文初始化過程中,就開始讀入插件的節(jié)點(diǎn)和我們配置的信息,同時(shí)使用反射技術(shù)生成對(duì)應(yīng)的插件實(shí)例,然后調(diào)用setProperties方法,設(shè)置我們配置的參數(shù),然后將插件的實(shí)例保存到配置的對(duì)象中,以便以后的讀取和使用它。插件的初始化是在Mybatis的初始化的時(shí)候進(jìn)行的初始化,而不是在使用的時(shí)候才被初始化,這樣可以提高性能。
3. 插件的代理和反射的設(shè)計(jì)
插件用的是責(zé)任鏈的模式實(shí)現(xiàn)的。
所謂責(zé)任鏈就是,一個(gè)對(duì)象在Mybatis中可能是四大對(duì)象(Excetor、StatementHandler、ParameterHandler、ResultHandler)中的一個(gè),在多個(gè)角色中傳遞,處在責(zé)任鏈上的任何角色都有處理它的機(jī)會(huì)。
作用:讓每一個(gè)責(zé)任鏈上的角色都有機(jī)會(huì)去攔截這個(gè)對(duì)象,在將來(lái)有新的角色也可以輕松攔截請(qǐng)求對(duì)象,進(jìn)行處理。
Mybatis的責(zé)任鏈?zhǔn)怯胕nterceptorChain去定義的。我們知道plugin方法是生成代理對(duì)象的方法,當(dāng)它取出插件的時(shí)候從Configuration對(duì)象中去取出。從第一對(duì)象開始,將對(duì)象傳遞給plugin方法,然后返回一個(gè)代理;如果存在第二個(gè)插件,那么我們就拿到第一個(gè)代理對(duì)象,傳遞給plugin方法在返回第一代理對(duì)象的代理….一次類推,有多少個(gè)攔截器就生成多少個(gè)代理對(duì)象。
在初始化的時(shí)候,我們一個(gè)個(gè)的加載插件實(shí)例,并用setProperties方法進(jìn)行初始化,我們可以使用Mybatis提供的Plugin.wrap方法來(lái)生成代理對(duì)象,在一層層的使用Invaction對(duì)象的proceed()方法來(lái)推動(dòng)代理對(duì)象的運(yùn)行。在多個(gè)插件的環(huán)境下,調(diào)度proceed()方法時(shí),Mybatis總是從最后一個(gè)代理對(duì)象運(yùn)行到第一代理對(duì)象,最后是真實(shí)被攔截的對(duì)象方法被運(yùn)行。
4.常用的工具類—MeteObject
在Mybatis插件中常用到一個(gè)工具類是MeteObject。
作用:可以有效的讀取或者修改一些重要對(duì)象的屬性。
常用的方法:
MeteObject forObject(Object object,ObjectFactoryobjectFactory,ObjectWrapperFactory objectWrapperFactory);
用于封裝對(duì)象,這個(gè)是已經(jīng)過時(shí)了,現(xiàn)在使用SystemMetaObject(Object obj)
Object getValue(String name)方法:用于獲取對(duì)象的屬性值,支持OGNL。
void setValue(String name,Object value)方法:用于修改對(duì)象的屬性值,支持OGNL。
在Mybatis對(duì)象中大量使用這個(gè)類進(jìn)行包裝,包括四大對(duì)象,使得我們可以通過它來(lái)給四大對(duì)象的某些屬性賦值從而滿足我們的需要。
5. 插件的開發(fā)步驟
(1)確定需要攔截的簽名
A:確定要攔截的對(duì)象
Exector對(duì)象:執(zhí)行SQL全過程,包含組裝參數(shù),組裝結(jié)果集返回和執(zhí)行SQL過程,都可以攔截,較為廣泛,一般在實(shí)際中用的不多。
StatementHandler對(duì)象:執(zhí)行SQL的過程,我們可以重寫執(zhí)行SQL的過程,實(shí)際常用攔截對(duì)象。
ParamterHandler對(duì)象:主要是攔截執(zhí)行SQL的參數(shù)組裝,可以重寫組裝參數(shù)規(guī)則。
ResultSetHandler對(duì)象:主要是攔截執(zhí)行結(jié)果的組裝,可以重寫組裝結(jié)果規(guī)則。
B:攔截的方法和參數(shù)
public interface StatementHandler{
Statementprepare(Connection connection)throws SQLException;
voidparameterize(Statement statement)throws SQLException;
void batch(Statement statement)throwsSQLException;
int update(Statementstatement)throws SQLException;
<E>List<E>Query(Statementstatement,ResultHandler resultHandler)throws SQLException;
BoundSql getBoundSql();
ParameterHandlergetParameterHandler();
}
定義插件簽名
@Inteceptor({@signature(type=Statement.class,method=”prepare”,args={Connection.class})
})
Public class Myplugin implements Interceptor{
}
(2)實(shí)現(xiàn)攔截的方法
package com.zl.mybatis.test;
import java.util.Properties;
import org.apache.ibatis.executor.Executor;
importorg.apache.ibatis.mapping.MappedStatement;
importorg.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
/**
*@author fab
*
*/
@Intercepts({ @Signature(type =Executor.class, // 確定攔截的對(duì)象
method = "update", // 確定攔截的方法
args = { MappedStatement.class,Object.class }// 確定攔截的參數(shù)
) })
public class MyPlugin implementsInterceptor {
Propertiesprops = null;
/**
* 代替攔截對(duì)象方法的內(nèi)容
* @param invocation
* 責(zé)任鏈對(duì)象
*/
@Override
publicObject intercept(Invocation invocation) throws Throwable {
//TODO Auto-generated method stub
System.out.println("before.......");
//如果當(dāng)前代理的是一個(gè)非代理對(duì)象,那么它就回調(diào)用真實(shí)攔截對(duì)象的方法,如果不是它將會(huì)調(diào)度下一個(gè)插件代理對(duì)象的invoke方法
Objectobject = invocation.proceed();
System.out.println("after.....");
returnobject;
}
/***
* 生成對(duì)象的代理,這用常用Mybatis提供的Plugin類的wrap方法
* @param target
* 被代理的對(duì)象
*/
@Override
publicObject plugin(Object target) {
//TODO Auto-generated method stub
//使用Mybatis提供的Plugin類生成代理對(duì)象
System.out.println("調(diào)用生成代理對(duì)象.....");
returnPlugin.wrap(target, this);
}
/***
* 獲取插件配置的屬性,在Mybatis的配置文件里面去配置
* @param props Mybatis的配置參數(shù)
*
*/
@Override
publicvoid setProperties(Properties props) {
//TODO Auto-generated method stub
System.out.println(props.get("dbType"));
this.props= props;
}
}
(3)配置
配置插件:
<plugins>
<plugininteceptor=” com.zl.mybatis.test.MyPlugin”>
<property name=”dbType” value=”MySQL”>
</plugin>
</plugins>
(4) 運(yùn)行結(jié)果
調(diào)用生成代理對(duì)象.....
before.......
調(diào)用生成代理對(duì)象.....
調(diào)用生成代理對(duì)象.....
調(diào)用生成代理對(duì)象.....
after.....
7.簡(jiǎn)單說
Mybatis插件的實(shí)現(xiàn)方法:
需要實(shí)現(xiàn)Interceptor接口,實(shí)現(xiàn)三個(gè)方法。
public Object intercept(Invocation invocation);
作用:插件的具體實(shí)現(xiàn)的地方,這是最復(fù)雜的地方。
public Object plugin(Object target);
作用:利用Mybatis自身的Plugin的Wrap()方法來(lái)獲得代理對(duì)象。
public void setProperties(Properties props);
作用:初始化一些基本設(shè)置,這些配置在Mybatis的.xml文件中體現(xiàn)。
需要明確:攔截那個(gè)對(duì)象,攔截那個(gè)方法,攔截那個(gè)參數(shù)。
8.總結(jié)
使用插件應(yīng)注意的六點(diǎn):
(1) 能不用插件盡量不用插件,因?yàn)樗鼘⑿薷腗ybatis的底層設(shè)計(jì)。
(2) 插件生成的是層層代理對(duì)象的責(zé)任鏈模式,通過反射方法運(yùn)行,性能不高,所以減少插件就能減少代理,從而提高系統(tǒng)的性能。
(3) 編寫插件需要了解Mybatis的運(yùn)行原理,了解四大對(duì)象及其方法的作用,準(zhǔn)確判斷需要攔截什么對(duì)象,什么方法,參數(shù)是什么,才能確定簽名如何編寫。
(4) 在插件中往往需要讀取和修改Mybatis映射器的對(duì)象屬性,你需要熟練掌握關(guān)于Mybatis映射器內(nèi)部組成的知識(shí)。
(5) 插件的編寫需要考慮全面,特別是多個(gè)插件層層代理的時(shí)候,要保證邏輯的正確性。
(6) 盡量小改動(dòng)Mybatis底層的東西,以減少錯(cuò)誤的發(fā)生。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注