mybatis拦截器

mybatis拦截器

一、拦截器介绍

Mybatis拦截器设计的初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。通过Mybatis拦截器我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。所以Mybatis拦截器的使用范围是非常广泛的。

使用介绍

@Intercepts({
        @Signature(type = Executor.class, method = "query",
                args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) })
public class DbFlowCtrlInterceptor implements Interceptor {

    private static volatile int count = 0;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        count++;
        System.out.println("当前查询数量=" + count);
        if (1000 < count) {
            System.out.println("超过阈值");
        }
        Object proceed = invocation.proceed();
        count--;
        return proceed;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

meybaits配置文件修改,plugin位置必须遵循下面顺序,在environments之前 configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?

<plugins>
        <plugin interceptor="maintest.DbFlowCtrlInterceptor"/>
</plugins>

二、实现原理

1、读取mybatis-config配置文件,解析plugins节点,将plugin中的拦截器添加到Configuration的InterceptorChain拦截器列表中。

	/**
     *
     * 功能描述: 解析配置文件拦截器配置
     *
     * @param parent
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
            for (XNode child : parent.getChildren()) {
                // 拿到拦截器的名称
                String interceptor = child.getStringAttribute("interceptor");
                // 如果拦截器需要进行属性配置的话,得到属性配置
                Properties properties = child.getChildrenAsProperties();
                // 生成拦截器实例对象
                Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
                // 添加拦截器属性
                interceptorInstance.setProperties(properties);
                // 将拦截器添加到配置中
                configuration.addInterceptor(interceptorInstance);
            }
        }
    }

2、Configuration创建Executor、StatementHandler、ResultSetHandler、ParameterHandler的实例时候如果存在拦截器,会通过动态代理的方式生成这些实例的代理对象

// 生成执行器代理对象
executor = (Executor) interceptorChain.pluginAll(executor);
// 生成statement处理代理对象
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
// 生成resultHandler代理对象
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
// 生成parameterHandler代理对象
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);

3、InterceptorChain 责任链的方式层层包装代理对象

	/**
     *
     * 功能描述: 生成代理对象,如果存在多个拦截器的话会生成代理对象的代理对象
     *
     * @param target
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
            // 生成代理对象,代理对象中对需要拦截的方法加入了拦截的处理逻辑
            target = interceptor.plugin(target);
        }
        return target;
    }

4、拦截器中的plugin方法

  // 动态代理包装被代理对象
  Plugin.wrap(target, this);

  /**
   *
   * 功能描述: 包装需要被代理的对象
   *
   * @param target
   * @param interceptor
   * @return
   * @see [相关类/方法](可选)
   * @since [产品/模块版本](可选)
   */
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    // 拿到拦截器中标注需要拦截的所有的接口
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      // 返回一个代理对象
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

5、Executor、StatementHandler、ResultSetHandler、ParameterHandler执行的时候如果存在拦截器,实际调用的是Plugin的invoke方法

  /**
   *
   * 功能描述: 代理对象执行的时候实际调用的方法
   *
   * @param proxy
   * @param method
   * @param args
   * @return
   * @see [相关类/方法](可选)
   * @since [产品/模块版本](可选)
   */
  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);
    }
  }
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值