• 【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的源码。先获取目标方法所在类需要拦截的所有方法,然后判断目标方法是否再里面,是的话就交给拦截器执行,不是的话直接执行目标方法。

  • 相关阅读:
    动态数组arraylist的使用
    第一次
    layui.mobile.css
    index.html
    Ansible部署配置
    微服务项目配置文件
    镜像挂载
    网卡设置
    获取内存信息
    超时方法
  • 原文地址:https://www.cnblogs.com/simple-flw/p/13182735.html
Copyright © 2020-2023  润新知