mybatis拦截器

时间:2020-9-3 作者:admin


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);
    }
  }
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。