mybatis 源码系列 组件之 plugin

本文详细解析了MyBatis中的Interceptor接口及其使用方法,通过自定义Interceptor实现拦截特定方法,影响MyBatis的行为。介绍了如何通过@Intercepts注解指定要拦截的方法,以及Plugin工具类的作用,展示如何生成代理类并调用自定义Interceptor的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Question:

. interceptor 怎么实现?

. interceptor 怎么 影响 mybatis 行为


首先 从 Interceptor 接口 入手:

public interface Interceptor {


  Object intercept(Invocation invocation) throws Throwable; // 对 某一个 target的某一个方法进行拦截


  Object plugin(Object target); //对target 进行包装,保证可以 被 intercept 方法调用


  void setProperties(Properties properties); // 使得 可以 对Interceptor 的某些属性进行修改


}


接着 我们 定义一个 自己的 interceptor:


@Intercepts(value = { @Signature(type = Comparable.class, method = "compareTo", args = { Object.class }) }) // 表示 只对 Comparable 接口的 compareTo 方法 进行拦截 
public class CustomerInterceptor implements Interceptor {


@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("intercept before"); // 拦截 前处理逻辑
Object result = invocation.proceed();//  可以 在 拦截前 逻辑中判断 是否 需要 调用 invocation.proceed()  以达到拦截的目的
System.out.println("intercept after");// 拦截 后 处理 逻辑
return result;
}


@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this); // Plugin 为一个 工具类,其wrap方法 会检查 target的 所有 interface,包括父类实现的接口,并根据 CustomerInterceptor  上的 @Intercepts 注解 判断是否 生成代理类,生成的代理类会调用 CustomerInterceptor的intercept 方法
}


@Override
public void setProperties(Properties properties) {


}


@SuppressWarnings("unchecked")
@Test
public void testCustomerInterceptor() {


Comparable<String> test = "abc123";


InterceptorChain interceptorChain = new InterceptorChain();
interceptorChain.addInterceptor(new CustomerInterceptor());
test = (Comparable<String>) interceptorChain.pluginAll(test);


System.out.println(test.compareTo("abc123"));
}
}



运行这个 test,即输出如下信息:

intercept before
intercept after
0


接着,介绍一下 Plugin 这个 工具类:

public class Plugin implements InvocationHandler {


  private Object target;
  private Interceptor interceptor;
  private 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) { //检查 target的 所有 interface,包括父类实现的接口,并根据 CustomerInterceptor  上的 @Intercepts 注解 判断是否 生成代理类,生成的代理类会调用 CustomerInterceptor的intercept 方法
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); // 获取 interceptor 类上 所需要 拦截的 接口 和 方法 
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap); // 获取 target 及其 父类 所实现的接口
    if (interfaces.length > 0) { // 如果 存在 需要 拦截的 接口, 则 反向代理 
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }


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


  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class); // 获取 interceptor 上的  Intercepts 标志 
    if (interceptsAnnotation == null) { // issue #251
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    } 
    Signature[] sigs = interceptsAnnotation.value(); 
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.get(sig.type());  // 获取  interceptor 上的  Intercepts 注解的 method 标志 
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
        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) {// 获取 target 及其 父类 所实现的接口,同时需要满足被signatureMap 包含  
    Set<Class<?>> interfaces = new HashSet<Class<?>>();
    while (type != null) {
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }


}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值