java MyBatis攔截器Inteceptor詳細介紹
有許多java初學(xué)者對于MyBatis攔截器Inteceptor不是很了解,在這里我來為各位整理下篇關(guān)于java中MyBatis攔截器Inteceptor詳解,
本文主要分析MyBatis的插件機制,實際就是Java動態(tài)代理實現(xiàn)的責(zé)任鏈模式實現(xiàn)。
根據(jù)官方文檔。Mybatis只允許攔截以下方法,這個決定寫攔截器注解簽名參數(shù)。
代碼如下
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) ParameterHandler (getParameterObject, setParameters) ResultSetHandler (handleResultSets, handleOutputParameters) StatementHandler (prepare, parameterize, batch, update, query)
攔截處理的源碼如下,其中interceptorChain.pluginAll(..)即為織入自定義攔截器:
代碼如下
/* org.apache.ibatis.session.Configuration類中方法 */
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
/* 攔截ParameterHandler*/
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 = (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 = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
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);
}
/* 攔截Executor*/
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
實現(xiàn)一個自定義攔截器只需實現(xiàn)Interceptor接口即可,大致代碼如下:
代碼如下
/* 注解表明要攔截哪個接口的方法及其參數(shù) */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class YourInterceptor implements Interceptor{
public Object intercept(Invocation invocation) throws Throwable{
doSomeThing();
/* 注:此處實際上使用Invocation.proceed()方法完成interceptorChain鏈的遍歷調(diào)用(即執(zhí)行所有注冊的Interceptor的intercept方法),到最終被代理對象的原始方法調(diào)用 */
return invocation.proceed();
}
/*生成成對目標target的代理,而@Intercepts的注解是在Plugin.wrap中用到*/
@Override
public Object plugin(Object target){
/* 當目標類是StatementHandler類型時,才包裝目標類,不做無意義的代理 */
return (target instanceof StatementHandler)?Plugin.wrap(target, this):target;
}
/*用于設(shè)置自定義的攔截器配置參數(shù)*/
@Override
public void setProperties(Properties properties){
}
}
其中,攔截調(diào)用的代碼均在Plugin.wrap中:
代碼如下
/* org.apache.ibatis.plugin.Plugin類 */
public class Plugin implements InvocationHandler {
/* 省略代碼... */
public static Object wrap(Object target, Interceptor interceptor) {
/* 此處即為獲取Interceptor的注解簽名 */
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
/* 獲取攔截目標類相匹配的接口 */
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
/* 使用jdk動態(tài)代理 */
return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));
}
return target;
}
/* 攔截目標類的所有方法的執(zhí)行都會變?yōu)樵诖藞?zhí)行 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
/* 執(zhí)行攔截器方法 */
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
/* 省略代碼... */
}
可以看到MyBatis的攔截器設(shè)計核心代碼還是比較簡單的,但是足夠靈活。實際使用時注意,不做無意義的代理(Plugin.wrap)。
相關(guān)文章
Idea如何配置Maven才能優(yōu)先從本地倉庫獲取依賴(親測方法有效)
對于Idea怎么配置Maven才能優(yōu)先從本地倉庫獲取依賴,網(wǎng)上說法有很多種,都不太靠譜,最終都沒有效果,最好的解決方法是通過修改maven配置文件settings.xml,本文給大家介紹的非常詳細,需要的朋友參考下吧2023-10-10
深入理解Java8新特性之接口中的默認方法和靜態(tài)方法
從Java8開始,程序允許在接口中包含帶有具體實現(xiàn)的方法,使用default修飾,這類方法就是默認方法。默認方法在接口中可以添加多個,并且Java8提供了很多對應(yīng)的接口默認方法,接下來讓我們一起來看看吧2021-11-11
SpringBoot集成ShedLock實現(xiàn)分布式定時任務(wù)流程詳解
ShedLock是一個鎖,官方解釋是他永遠只是一個鎖,并非是一個分布式任務(wù)調(diào)度器。一般shedLock被使用的場景是,你有個任務(wù),你只希望他在單個節(jié)點執(zhí)行,而不希望他并行執(zhí)行,而且這個任務(wù)是支持重復(fù)執(zhí)行的2023-02-02

