有許多java初學者對于MyBatis攔截器Inteceptor不是很了解,在這里我來為各位整理下篇關于java中MyBatis攔截器Inteceptor詳解,
本文主要分析MyBatis的插件機制,實際就是Java動態(tài)代理實現(xiàn)的責任鏈模式實現(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ù)*/ @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的攔截器設計核心代碼還是比較簡單的,但是足夠靈活。實際使用時注意,不做無意義的代理(Plugin.wrap)。
新聞熱點
疑難解答