更新時(shí)間:2021-07-16 15:56:40 來源:動(dòng)力節(jié)點(diǎn) 瀏覽1536次
當(dāng)我們使用Mybatis的時(shí)候,總會(huì)使用到各種插件,如PageHelper(分頁插件)等,當(dāng)我們需要自定義插件來改變,就必須了解插件的實(shí)現(xiàn)原理。
Mybatis插件又稱攔截器,Mybatis采用責(zé)任鏈模式,通過動(dòng)態(tài)代理組織多個(gè)插件(攔截器),通過這些插件可以改變Mybatis的默認(rèn)行為。MyBatis允許你在已映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用。默認(rèn)情況下,MyBatis允許使用插件來攔截的方法調(diào)用包括:

上圖Mybatis框架的整個(gè)執(zhí)行過程。Mybatis插件能夠?qū)t四大對(duì)象進(jìn)行攔截,可以包含到了Mybatis一次會(huì)議的所有操作。可見Mybatis的的插件很強(qiáng)大。
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor, autoCommit);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
這4個(gè)方法實(shí)例化了對(duì)應(yīng)的對(duì)象之后,都會(huì)調(diào)用interceptorChain的pluginAll方法,那么下面我們?cè)趤砜磒luginAll干了什么
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
原來這個(gè)pluginAll方法就是遍歷所有的攔截器,然后順序執(zhí)行我們插件的plugin方法,一層一層返回我們?cè)瓕?duì)象(Executor/ParameterHandler/ResultSetHander/StatementHandler)的代理對(duì)象。當(dāng)我們調(diào)用四大接口對(duì)象的方法時(shí)候,實(shí)際上是調(diào)用代理對(duì)象的響應(yīng)方法,代理對(duì)象又會(huì)調(diào)用四大接口對(duì)象的實(shí)例。
攔截器Interceptor
Mybatis的插件實(shí)現(xiàn)要實(shí)現(xiàn)Interceptor接口,我們看下這個(gè)接口定義的方法。
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
這個(gè)接口只聲明了三個(gè)方法。
官方推薦插件開發(fā)方式
@Intercepts({@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TestInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget(); //被代理對(duì)象
Method method = invocation.getMethod(); //代理方法
Object[] args = invocation.getArgs(); //方法參數(shù)
// do something ...... 方法攔截前執(zhí)行代碼塊
Object result = invocation.proceed();
// do something .......方法攔截后執(zhí)行代碼塊
return result;
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
簡(jiǎn)單編寫Mybatis插件
注:MyBatis默認(rèn)沒有一個(gè)攔截器接口的實(shí)現(xiàn)類,開發(fā)者可以實(shí)現(xiàn)符合自己需求的攔截器。
下面的MyBatis官網(wǎng)的一個(gè)攔截器實(shí)例:
@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
全局xml配置:
<plugins>
<plugin interceptor="org.format.mybatis.cache.interceptor.ExamplePlugin"></plugin>
</plugins>
這個(gè)攔截器攔截Executor接口的update方法(其實(shí)也就是SqlSession的新增,刪除,修改操作),所有執(zhí)行executor的update方法都會(huì)被該攔截器攔截到。
以上就是動(dòng)力節(jié)點(diǎn)小編介紹的"Mybatis插件原理",希望對(duì)大家有幫助,想了解更多可查看Mybatis視頻教程。動(dòng)力節(jié)點(diǎn)在線學(xué)習(xí)教程,針對(duì)沒有任何Java基礎(chǔ)的讀者學(xué)習(xí),讓你從入門到精通,主要介紹了一些Java基礎(chǔ)的核心知識(shí),讓同學(xué)們更好更方便的學(xué)習(xí)和了解Java編程,感興趣的同學(xué)可以關(guān)注一下。
Java實(shí)驗(yàn)班
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
Java就業(yè)班
有基礎(chǔ) 直達(dá)就業(yè)
Java夜校直播班
業(yè)余時(shí)間 高薪轉(zhuǎn)行
Java在職加薪班
工作1~3年,加薪神器
Java架構(gòu)師班
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)