MyBatis拦截器Inteceptor


转载自JavaTalk – zhouhaocheng.com


MyBatis的插件机制,实际就是Java动态代理实现的责任链模式实现。

根据官方文档。Mybatis只允许拦截以下方法,这个决定写拦截器注解签名参数。

  • 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;
}

实现一个自定义拦截器只需实现Interceptor接口即可,大致代码如下:

/* 注解表明要拦截哪个接口的方法及其参数 */
@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链的遍历调用(即执行所有注册的Interceptor的intercept方法),到最终被代理对象的原始方法调用 */
    return invocation.proceed();
  }

   /*生成成对目标target的代理,而@Intercepts的注解是在Plugin.wrap中用到*/
  @Override
  public Object plugin(Object target){
     /* 当目标类是StatementHandler类型时,才包装目标类,不做无意义的代理 */
    return (target instanceof StatementHandler)?Plugin.wrap(target, this):target;
  }

   /*用于设置自定义的拦截器配置参数*/
  @Override
  public void setProperties(Properties properties){
  }
}

其中,拦截调用的代码均在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动态代理 */
      return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  /* 拦截目标类的所有方法的执行都会变为在此执行 */
  @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)) {
        /* 执行拦截器方法 */
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

  /* 省略代码... */

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值