Mybatis之Interceptor

1,736 阅读2分钟

首先看下使用方法。 在Interceptor的实现类上加上@Intercepts注解即可,@Intercepts注解内部是一个@Signature的数组,每个@Signature可以指定拦截类的class,method。从plugin方法往上追溯,可以发现在每次sql查询中,生成ResultSetHandler、StatementHandler和Executor,都有一行代码:interceptorChain.pluginAll(executor)。这里是plugin方法的触发点。interceptorChain在初始化Configuration的时候已经加载,此处代码旨在由原先的ResultSetHandler、StatementHandler和Executor对象生成一个个对应的代理对象,在后续调用的时候,会一次调用代理的invoke方法。

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class SwitchInterceptor implements Interceptor {
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
}

Interceptor

首先是Interceptor接口类

public interface Interceptor {
  // 拦截器主方法,可自定义实现intercept方法的逻辑,对sql进行拦截处理
  Object intercept(Invocation invocation) throws Throwable;
  // 使用方需要实现plugin方法,由被代理target封装为代理对象Plugin
  Object plugin(Object target);
    
  void setProperties(Properties properties);

}

###Plugin 上面提到的生成代理对象就是调用了wrap方法。

public class Plugin implements InvocationHandler {

  private final Object target;
  private final Interceptor interceptor;
  private final Map<Class<?>, Set<Method>> signatureMap;

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

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

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //获取需要被代理的方法集合,在@Inteceptor中指定
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      //判断被代理的方法是否在@Inteceptor中指定的方法集合中
      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);
    }
  }

  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    //获取Interceptor实现类上的Interceptor注解
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
    }
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
    for (Signature sig : sigs) {
     
      Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
      try {
        //@Signature中配置的被代理对象的方法
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    //返回指定被代理对象的需要被代理的方法集合
    return signatureMap;
  }

  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<>();
    while (type != null) {
      //获取type类实现的接口数组,如果是extend别的类,则返回的是空数组
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }

}