Mybatis是當今最普遍使用的數據持久化框架,這點拒絕反駁。
Mybatis調用鏈:
SqlSessionInterceptor.invoke()
DefaultSqlSessionFactory.openSession(executorType): DefaultSqlSession
->Configuration.newExecutor(): sqlSession //interceptorChain.pluginAll(excutor);
DefaultSqlSession.selectList(String statement, Object parameter, RowBounds rowBounds)
->CachingExecutor.query(); =>SimpleExecutor.query() //queryFromDatabase
->Configuration.newStatementHandler(): StatementHandler
->new RoutingStatementHandler();
->configuration.newParameterHandler() //interceptorChain.pluginAll(parameterHandler);
->configuration.newResultSetHandler(); //interceptorChain.pluginAll(resultSetHandler);
->interceptorChain.pluginAll(statementHandler); //*****註冊插件(共四類)、生成代理類
->Plugin.wrap(target, this); ->Proxy.newProxyInstance(cl, interfaces, new Plugin(target, interceptor, signatureMap));
->RoutingStatementHandler.prepare(connection, timeout);
->RoutingStatementHandler.parameterize(stmt)
可見,mybatis可以再四個地方使用插件:Executor、ParameterHandler、ResultHandler、StatementHandler。大部分情況下我們都會實現Executor的插件,比如分頁、改變sql參數等。自定義一個插件主要三步:
1、繼承 org.apache.ibatis.plugin.Interceptor,並實現方法 Object intercept(Invocation invocation),mybatis_3.5之前還要實現Object plugin(Object target) ;
2、聲明攔截的類型,如:@Intercepts({
@Signature(type=Executor.class, method="update", args={ MappedStatement.class, Object.class }),
@Signature(type=Executor.class, method="query", args={ MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}
3、加入spring容器中,特別要注意它處於InterceptorChain鏈中的位置,這樣會直接影響執行效果,因爲mybatis的攔截器執行順序與聲明的位置相反。
最後特別要說明一個網上流行的分頁組件 github.PageHelper,這哥們做的特別噁心,將自己放在最後一個,即第一個執行,並且硬性改變了executor的執行方法,使得後面攔截器可能會無法執行,具體看他的實現:
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
.....
if (args.length == 4) {
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
checkDialectExists();
List resultList;
if (!dialect.skip(ms, parameter, rowBounds)) {
if (dialect.beforeCount(ms, parameter, rowBounds)) {
Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
if (!dialect.afterCount(count, parameter, rowBounds)) {
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
}
}
resultList = ExecutorUtil.pageQuery(dialect, executor,
ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
} else {
resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
return dialect.afterPage(resultList, parameter, rowBounds);
} finally {
....
}
}
從這裏看出,它破壞了chain的執行順序,並沒有調用 invocation.proceed(),而是直接調用了留個參數的query方法。
說完了。