java MyBatis攔截器Inteceptor詳細(xì)介紹
有許多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(); } /*生成成對目標(biāo)target的代理,而@Intercepts的注解是在Plugin.wrap中用到*/ @Override public Object plugin(Object target){ /* 當(dāng)目標(biāo)類是StatementHandler類型時,才包裝目標(biāo)類,不做無意義的代理 */ 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(); /* 獲取攔截目標(biāo)類相匹配的接口 */ 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; } /* 攔截目標(biāo)類的所有方法的執(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)文章
Java String 和StringBuffer的詳解及區(qū)別
這篇文章主要介紹了Java String 和StringBuffer的詳解及區(qū)別的相關(guān)資料,需要的朋友可以參考下2017-05-05Java?Stream.reduce()用法詳細(xì)解析
Stream API提供了豐富的中間函數(shù),歸并函數(shù)和終端函數(shù),這些函數(shù)還支持并行化執(zhí)行,下面這篇文章主要給大家介紹了關(guān)于Java?Stream.reduce()用法的相關(guān)資料,需要的朋友可以參考下2022-12-12springMVC 用戶登錄權(quán)限驗證實現(xiàn)過程解析
這篇文章主要介紹了springMVC 用戶登錄權(quán)限驗證實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11JDBC如何訪問MySQL數(shù)據(jù)庫,并增刪查改
這篇文章主要介紹了JDBC如何訪問MySQL數(shù)據(jù)庫,幫助大家更好的理解和學(xué)習(xí)java與MySQL,感興趣的朋友可以了解下2020-08-08