美文网首页
mybatis插件

mybatis插件

作者: 我是许仙 | 来源:发表于2020-07-02 20:17 被阅读0次

    mybatis插件

    使用

    1. 注册插件

      <plugins>
          <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
      </plugins>
      
    2. 编写插件类 实现org.apache.ibatis.plugin.Interceptor接口

    3. 添加注解

      @Intercepts(
              {
                      @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
                      @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
              }
      )
      public class InterceptorTwo implements Interceptor
      
      1. Signature
      @Documented
      @Retention(RetentionPolicy.RUNTIME)
      @Target({})
      public @interface Signature {
        //拦截的方法类型
        Class<?> type();
        //拦截的方法名字
        String method();
        //方法中的参数类型集合
        Class<?>[] args();
      }
      
      1. Intercepts

        @Documented
        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.TYPE)
        public @interface Intercepts {
          //返回需要拦截的方法签名列表
          Signature[] value();
        }
        

    插件初始化流程

    1. 项目启动加载配置文件

    2. 解析配置文件

    3. 解析plugins标签

      XMLConfigBuilder

      private void parseConfiguration(XNode root) {
          //解析plus标签
          pluginElement(root.evalNode("plugins"));
      }    
      
    4. 循环插入插件类

      XMLConfigBuilder

      //解析拦截器类并插入list集合
      private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
          //循环获取子类并插入一个List 配置文件中plugin的顺序与执行的顺序相反
          // child = <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
          for (XNode child : parent.getChildren()) {
            //获取interceptor属性
            String interceptor = child.getStringAttribute("interceptor");
            //获取参数
            Properties properties = child.getChildrenAsProperties();
            //这句话等同于Class.forName("class").newInstance()通过反射生成一个对象
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
            interceptorInstance.setProperties(properties);
            //List<Interceptor> interceptors = new ArrayList<>(); 集合插入对象
            configuration.addInterceptor(interceptorInstance);
          }
        }
      

    插件调用流程

    1. 获取session

      SqlSession sqlSession1 = sqlSessionFactory.openSession();
      
    2. 新增执行器

      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
          //新建一个执行器
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
      }
      
    3. 初始化执行器

      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        ....
        //通过循环代理 一层层的包装来实现调用链
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
      
    4. 动态生成拦截器代理

      public Object pluginAll(Object target) {
          //先进后出
          for (Interceptor interceptor : interceptors) {
           //动态生成代理类等于Proxy.newProxyInstance 像洋葱一样 代理了另一个代理类
            target = interceptor.plugin(target);
          }
          return target;
        }
      
      public interface Interceptor {
        Object intercept(Invocation invocation) throws Throwable;
        //default 关键字方法的默认实现 实现类无需实现这个方法  
        default Object plugin(Object target) {
          //静态工厂方法获取一个代理类
          return Plugin.wrap(target, this);
        }
      }
      

      Plugin implements InvocationHandler

      //静态工厂方法 生成代理类
      public static Object wrap(Object target, Interceptor interceptor) {
        //通过解析拦截器Intercepts注解与Signature注解
        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 {
          //判断此拦截器需要拦截的方法是否与需要执行的方法相同 
          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);
        }
      }
      

    插件调用链视图

    image-20200702165404163.png

    精简代码核心流程

    看一下删除了各种设计模式之后的代码 只是展示思路用

    public void testInterceptor() {
        Object target = 12;
        List<Interceptor> interceptorList = new ArrayList<>();
        interceptorList.add(new InterceptorOne());
        interceptorList.add(new InterceptorTwo());
        for(Interceptor interceptor : interceptorList){
            //管理类
            PluginInvocation pluginInvocation = new PluginInvocation(target, interceptor);
            //代理类
            target =  Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), pluginInvocation);
        }
        //模拟触发代理
        System.out.println(target.equals(12));
    }
    

    InterceptorOne

    public class InterceptorOne implements Interceptor {
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("执行拦截器 1");
            return null;
        }
    }
    

    InterceptorTwo

    public class InterceptorTwo implements Interceptor {
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("执行拦截器2");
            return null;
        }
    }
    

    PluginInvocation

    public class PluginInvocation implements InvocationHandler {
    
        //被代理对象
        private Object targetObject;
        private Interceptor interceptor;
    
        public PluginInvocation(Object o, Interceptor interceptor) {
            this.targetObject = o;
            this.interceptor = interceptor;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //执行拦截器逻辑 这里会有判断是否需要拦截的逻辑
            interceptor.intercept(null);
            //执行目标对象的方法
            return method.invoke(targetObject, args);
        }
    
    }
    

    pageHelper插件怎么分页的

    什么对象可以被拦截?官网如下

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

    • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

      (对应插件调用流程-3.初始化执行器 的时候会用代理模式包装Executor对象)

    • ParameterHandler (getParameterObject, setParameters)

    • ResultSetHandler (handleResultSets, handleOutputParameters)

    • StatementHandler (prepare, parameterize, batch, update, query)

    应用场景

    水平分表 权限控制 数据的加解密 分页插件

    相关文章

      网友评论

          本文标题:mybatis插件

          本文链接:https://www.haomeiwen.com/subject/ozhdqktx.html