【Mybatis源码阅读】Mybatis的插件体系

Mybatis的插件体系

​ Mybatis的插件体系是基于一个动态代理加要给责任链设计模式的运用。

Mybatis插件的源代码都在plugin目录下面。

/**
 * @author Clinton Begin
 */
// 拦截器    // Invocation:调用
  // 这是插件接口,所有插件需要实现该接口
public interface Interceptor {
  /**
   * 该方法内是拦截器拦截到目标方法时的操作
   * @param invocation 拦截到的目标方法的信息
   * @return 经过拦截器处理后的返回结果
   * @throws Throwable
   */
  Object intercept(Invocation invocation) throws Throwable;

  /**
   * 用返回值替代入参对象。
   * 通常情况下,可以调用Plugin的warp方法来完成,因为warp方法能判断目标对象是否需要拦截,并根据判断结果返回相应的对象来替换目标对象
   * @param target MyBatis传入的支持拦截的几个类(ParameterHandler、ResultSetHandler、StatementHandler、Executor)的实例
   * @return 如果当前拦截器要拦截该实例,则返回该实例的代理;如果不需要拦截该实例,则直接返回该实例本身
   */
  default Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }

  /**
   * 设置拦截器的属性
   * @param properties 要给拦截器设置的属性
   */
  default void setProperties(Properties properties) {
    // NOP
  }

}

​ 首先看看interface接口,所有的插件首先实现该接口。具体的方法的含义再源码中用中文加上了注释。

InterceptorChain 我们看看这个类,他是所有的插件的一个对象化的类。

 /**
     * 向所有的拦截器链提供目标对象,由拦截器链给出替换目标对象的对象
     * @param target 目标对象,是MyBatis中支持拦截的几个类(ParameterHandler、ResultSetHandler、StatementHandler、Executor)的实例
     * @return 用来替换目标对象的对象
     */
    public Object pluginAll(Object target) {
        // 依次交给每个拦截器完成目标对象的替换工作
        for (Interceptor interceptor : interceptors) {
            target = interceptor.plugin(target); //这样就是层层代理,点责任链的意思了
        }
        return target;
    }

​ 这个pluginAll方法就是给target对象进行代理的方法,通过循环依次对目标对象进行层层代理,生成最终的代理对象。

Mybatis会在这个几个地方执行ParameterHandler、ResultSetHandler,StatementHandler、Executor这个pluginAll方法。所有执行这几个累中的方法会执行拦截器。

动态代理类

Plugin是Mybatis的动态代理类,这个类实现了InvocationHandler接口,当代理类执行的时候实际执行就是代理类的invoke方法。

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

这里就是invoke的源码。先获取目标方法所在类需要拦截的所有方法,然后判断目标方法是否再里面,是的话就交给拦截器执行,不是的话直接执行目标方法。

posted @ 2020-10-26 00:10  一懒众衫小QAQ  阅读(63)  评论(0编辑  收藏  举报