`

Mybatis 插件原理

阅读更多
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}

方法签名接口.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
  /**
   * Returns the java type.
   *
   * @return the java type
   */
  Class<?> type();

  /**
   * Returns the method name.
   *
   * @return the method name
   */
  String method();

  /**
   * Returns java types for method argument.
   * @return java types for method argument
   */
  Class<?>[] args();
}

注入点.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
  /**
   * Returns method signatures to intercept.
   *
   * @return method signatures
   */
  Signature[] value();
}


扩展 Plugin 必须实现该接口.
public interface Interceptor {

  Object intercept(Invocation invocation) throws Throwable;

  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  default void setProperties(Properties properties) {
    // NOP
  }

}

Invocation 可以理解为会话,也就是说其会封装调用的方法,参数等数据. 类似于 Dubbo 中的 Invocation 的概念.
public class Invocation {

  private final Object target;
  private final Method method;
  private final Object[] args;

  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }

  public Object getTarget() {
    return target;
  }

  public Method getMethod() {
    return method;
  }

  public Object[] getArgs() {
    return args;
  }

  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }

}

InterceptorChain 字面含义是 Interceptor 链. 就是将所有的 Interceptor 组成一条链,这个和 dubbo 中 filter 的设计有些类似.
public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}

我们看下 Plugin 类中比较重要的一个方法.
private static Map<Class<?>, Set<Method>> getSignatureMap(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 {
        // 如果自定义过插件的话,就知道这里其实就是获取的 Interceptor 注解里面定义的方法.
        // 换句话说该方法就是被拦截的方法,这时候通过代理,就可以达到我们的目的.
        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;
  }

看下核心方法.
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;
  }

代理执行的方法.
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);
    }
  }

看到这里,是不是有一个疑问?如果代理执行了,那怎么回到我们的 query 方法了?
答案 Interceptor 中.
invocation.proceed() 方法会让程序回到最终的方法上.
public Object intercept(Invocation invocation) throws Throwable {
    return invocation.proceed();
  }
0
2
分享到:
评论

相关推荐

    02-02-03-MyBatis插件原理及Spring集成1

    总的来说,理解 MyBatis 插件原理和自定义插件的编写,可以帮助我们更好地扩展 MyBatis 的功能,实现更灵活的数据库操作。同时,掌握 Spring 集成 MyBatis 的方法,可以确保事务管理和资源管理的正确性,提升系统的...

    深入浅出MyBatis技术原理与实战(高清带目录版)

    《深入浅出MyBatis技术原理与实战》分为3 个部分,依次介绍了MyBatis 的基础应用、原理及插件开发、实践应用,使读者能够由浅入深、循序渐进地掌握MyBatis 技术。首先,《深入浅出MyBatis技术原理与实战》在官方API ...

    mybatis插件直接可用

    而"Idea MyBatis插件"则是针对IntelliJ IDEA这个强大的Java集成开发环境设计的一款增强工具,它可以提供更高效、便捷的MyBatis开发体验。 该插件的直接可用性意味着用户无需复杂设置,只需按照指定路径将其放置在...

    《深入浅出MyBatis技术原理与实战》高清完整PDF下载

    接着介绍了MyBatis的运行原理和插件开发并配有一个完整的插件例子。为了增加实用性,作者还介绍了MyBatis-Spring项目,使得读者能够学习到如何把MyBatis整合到Spring项目中,最后作者还将讲解一些常用实例,比如Blob...

    深入浅出MyBatis技术原理与实战

    , 《深入浅出MyBatis技术原理与实战》分为3 个部分,依次介绍了MyBatis 的基础应用、原理及插件开发、实践应用,使读者能够由浅入深、循序渐进地掌握MyBatis 技术。首先,《深入浅出MyBatis技术原理与实战》在官方...

    Mybatis系列教程Mybatis插件共8页.pdf.z

    这个教程可能深入解析了Mybatis插件的原理、安装、配置以及如何利用它们优化数据库操作。 【描述】"Mybatis系列教程Mybatis插件共8页.pdf.zip" 描述了教程的形式和大致内容。它是以PDF文档的形式存在,且压缩包内...

    深入浅出MyBatis技术原理与实战.pdf

     《深入浅出MyBatis技术原理与实战》分为3个部分,依次介绍了MyBatis的基础应用、原理及插件开发、实践应用,使读者能够由浅入深、循序渐进地掌握MyBatis技术。首先,《深入浅出MyBatis技术原理与实战》在官方API的...

    MyBatis自定义插件原理

    "MyBatis自定义插件原理" MyBatis自定义插件原理是指在MyBatis框架中,使用InterceptorChain和Plugin两个核心组件来实现自定义插件的功能。在MyBatis中,InterceptorChain是插件链的核心组件,而Plugin是插件的实现...

    深入浅出MyBatis技术原理与实战.杨开振(详细书签)

    还会涉及到MyBatis的插件机制,如PageHelper分页插件,以实现高效的分页查询。 总的来说,《深入浅出MyBatis技术原理与实战》会深入讲解MyBatis的各个方面,包括基本原理、配置、SQL映射、结果映射、事务管理、动态...

    一图读懂mybatis插件plugin原理

    本篇文章将通过解析“一图读懂mybatis插件plugin原理”来深入理解MyBatis Plugin的工作机制。 首先,我们需要了解MyBatis Plugin的基本概念。Plugin是MyBatis中用于拦截SqlSession方法的组件,它基于Java的动态代理...

    MyBatis 插件.zip

    首先,让我们深入了解 MyBatis 插件的工作原理。MyBatis 的插件基于 Java 的动态代理机制,它允许我们在特定的执行点(如:执行 SQL 前后、参数设置前后、结果映射前后等)插入自定义的行为。我们可以通过实现 `...

    深入浅出MyBatis技术原理与实战(高清带书签)

    本书还会讲解MyBatis的插件机制,开发者可以通过自定义插件拦截执行的SQL语句,进行日志记录、性能分析等操作。此外,MyBatis的缓存机制也是其高效性能的关键,分为一级缓存和二级缓存,可以有效减少对数据库的访问...

Global site tag (gtag.js) - Google Analytics