zoukankan      html  css  js  c++  java
  • Mybatis插件的使用以及源码分析

     Mybatis支持那些类型插件
    • Executor (update, query, flushStatements, commit, rollback, 
      getTransaction, close, isClosed)
    • ParameterHandler (getParameterObject, setParameters)
    • ResultSetHandler (handleResultSets, handleOutputParameters)
    • StatementHandler (prepare, parameterize, batch, update, query)
    如何使用插件
    •     自定义插件实现org.apache.ibatis.plugin.Interceptor接口
    •     通过 @Intercepts注解表明插件增强的类型,方法
    @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
    public class SQLStatsInterceptor implements Interceptor {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            System.out.println("mybatis intercept sql:" + sql);
            return invocation.proceed();
        }
    
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
    
        @Override
        public void setProperties(Properties properties) {
            String dialect = properties.getProperty("dialect");
           System.out.println("mybatis intercept dialect:"+dialect);
        }
    }
    

      

    •       config.xml中配置插件
    <plugins>
        <plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">
          <property name="pluginProperty" value="100"/>
        </plugin>
        <plugin interceptor="org.apache.ibatis.builder.SQLStatsInterceptor">
          <property name="dialect" value="test"/>
        </plugin>
      </plugins>
    

      

        为什么只支持上述的四种类型的插件呢?
      只是因为在Configuration中定义了如下几个方法:
     /**
       *plugin在此对ParameterHandler进行增强
       */
      public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
      }
    
      /**
       *plugin在此对ResultSetHandler进行增强
       */
      public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
          ResultHandler resultHandler, BoundSql boundSql) {
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
        return resultSetHandler;
      }
      /**
       *plugin在此对StatementHandler进行增强
       */
      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }
    
      public Executor newExecutor(Transaction transaction) {
        return newExecutor(transaction, defaultExecutorType);
      }
      /**
       *plugin在此对Executor进行增强
       */
      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    

      

    增强实现的源码
      在解析config.xml文件的时候会将<plugins>中配置的插件会添加到Configuration中到interceptorChain中
     private void pluginElement(XNode parent) throws Exception {
        //如果存在plugings节点
        if (parent != null) {
          //遍历所有子节点
          for (XNode child : parent.getChildren()) {
            //获取interceptor属性
            String interceptor = child.getStringAttribute("interceptor");
            Properties properties = child.getChildrenAsProperties();
            //创建Interceptor实例
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            //设置属性
            interceptorInstance.setProperties(properties);
            //向拦截器链注入拦截器
            configuration.addInterceptor(interceptorInstance);
          }
        }
      }
    
    public void addInterceptor(Interceptor interceptor) {
        interceptorChain.addInterceptor(interceptor);
      }
    

      

    SimpleExecutor中执行doUpdate方法是会通过configuration去创建一个StatementHandler
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
          log.trace(" SimpleExecutor ms.getConfiguration()");
          Configuration configuration = ms.getConfiguration();
          log.trace("SimpleExecutor configuration.newStatementHandler");
          StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.update(stmt);
        } finally {
          closeStatement(stmt);
    

      

    上文已经列出来了,在newStatementHandler中会对StatementHandler进行增强
    /**
       *plugin在此对StatementHandler进行增强
       */
      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }
    

      

    /**
     * 拦截器链
     * @author Clinton Begin
     */
    public class InterceptorChain {
    
        //拦截器列表
        private final List 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 getInterceptors() {
            return Collections.unmodifiableList(interceptors);
        }
    
    }
    @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
    

      

    通过上文中发现似乎所有拦截器都进行了增强处理,事实上不是这样都,具体使用哪些拦截器对接口进行增强是如下方法中处理的。同时可以发现 Plugin实现了InvocationHandler接口,也就是调用处理也是在该类中完成的,下面主要就是Plugin中的实现源码
    //对目标对象使用过interceptor进行增强
      public static Object wrap(Object target, Interceptor interceptor) {
        //获取拦截类,方法映射表
        Map<class<?>, Set> 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;
      }
    
     private static Map<class<?>, Set> getSignatureMap(Interceptor interceptor) {
        //获取类上@Intercepts的注解
        Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
        // 如果插件上没有@Intercepts注解,抛出异常
        if (interceptsAnnotation == null) {
          throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
        }
        //@Intercepts注解中所有签名
        Signature[] sigs = interceptsAnnotation.value();
        Map<class<?>, Set> signatureMap = new HashMap<class<?>, Set>();
        //遍历所有签名
        for (Signature sig : sigs) {
          //根据类型从签名映射表中获取方法集合
          Set methods = signatureMap.get(sig.type());
          if (methods == null) {
            //如果方法集合为null,则创建一个空集合并放入到映射表中
            methods = new HashSet();
            signatureMap.put(sig.type(), methods);
          }
          try {
            //根据方法名称,参数类型列表从指定的class中获取方法
            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;
      }
    

      

    这次可以发现通过JDK的动态代理进行了增强,在进行方法调用对时候会执行一下方法,在该方法中判断当前方法是否需要增强,如果需要就会调用interceptor 进行增强处理

    @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          Set 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);
        }
      }
    

      

    这次就简单介绍了Mybatis 插件功能的使用和实现方式
  • 相关阅读:
    Django rest framework Pagination(分页)
    Vue的mixin的一点使用(table的头部sticky的实现)
    spark streaming中维护kafka偏移量到外部介质
    spring-cloud-gateway负载普通web项目
    JdbcTemplate实体映射
    Sqoop葵花宝典
    kettle web化
    Summer Project
    Spark执行失败时的一个错误分析
    spark load data from mysql
  • 原文地址:https://www.cnblogs.com/wei-zw/p/8930871.html
Copyright © 2011-2022 走看看