美文网首页
原生Mybatis源码简析(下)

原生Mybatis源码简析(下)

作者: Hogantry | 来源:发表于2019-04-13 15:40 被阅读0次

    在上一篇文章中(原生Mybatis源码简析(上)),我们介绍了原生Mybatis的初始化,以及Mapper接口的运行原理。在介绍到Executor类具体执行SQL时,就没有继续下去了,现在我们继续将剩下的流程梳理一下。

    1、概述

    在具体介绍代码流程之前,我们得先介绍下如下四个比较重要的类,他在接下来的代码分析中占有重要的地位。

    1. Executor:Executor创建StatementHandler对象;
    2. StatementHandler: 设置sql语句,预编译,设置参数等相关工作,以及执行增删改查方法;
    3. ParameterHandler: 设置预编译参数;
    4. ResultHandler: 处理查询结果集;
      其中在设置参数和处理查询结果时,都是依赖TypeHandler,进行数据库类型和javaBean类型的映射

    2、流程介绍

    从上篇文章中,我们知道,在获取SqlSession的同时,其实已经生成了Executor对象了,并且是作为属性设置给了SqlSession对象。

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    

    我们看下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);
        }
       // 插件相关的代码,先记住这里,后面分析Mybatis插件机制时,再重点分析
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    

    这里executorType默认是simple,而Mybatis的二级缓存机制一般很少启用(一般都是自己在业务层通过redis等实现缓存机制),所以这里默认是返回SimpleExecutor类的实例。
    现在再回到上篇文章最后的代码部分,DefaultSqlSession的selectList方法中,调用了executor的query方法。该方法会调用到BaseExecutor的query方法,不考虑一级缓存,最后会来到SimpleExecutor的doQuery方法


    流程图
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    

    在该方法中,具体SQL语句的执行会有SimpleExecutor对象委托给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;
      }
    

    RoutingStatementHandler对象会也会根据statementType属性实例化对应的StatementHandler对象

      public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
        switch (ms.getStatementType()) {
          case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    
      }
    

    MappedStatement的StatementType属性默认是PREPARED(可以在MappedStatement实例化方法中看到),这里自然是实例化PreparedStatementHandler对象

      protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
    
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();
    
        if (boundSql == null) { // issue #435, get the key before calculating the statement
          generateKeys(parameterObject);
          boundSql = mappedStatement.getBoundSql(parameterObject);
        }
    
        this.boundSql = boundSql;
    
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
      }
    

    这里可以看出,在初始化StatementHandler的时候,就会同时实例化parameterHandler和resultSetHandler,并设置为StatementHandler的属性。

      public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        // 这里是第三次出现关于插件的代码,后续会分析
        parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
      }
    
      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;
      }
    

    我们再回到SimpleExecutor的doQuery方法中,先调用了prepareStatement方法,完成SQL参数化配置

    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
      }
    

    然后调用了StatementHandler的query方法

      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.handleResultSets(ps);
      }
    

    之后就是jdbc的套路了,就不再继续分析了。下面再看下Mybatis的插件机制

    3、插件机制

    在上一篇文章中,我们知道在初始化Configuration对象时,会初始化InterceptorChain对象,该对象中的interceptors属性中保存了所有的插件对象。在上面的代码介绍了,我们一共发现了四处,使用interceptorChain属性的pluginAll方法,这也就是为什么Mybatis支持Executor、StatementHandler、ParamaterHandler、ResultsetHandler四个对象的插件拦截机制的原因了。我们看下plugAll方法

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

    很简单,就是循环遍历执行Interceptor的plugin方法,而我们在实现一个插件时,通过继承Interceptor接口,实现plugin方法都是如下实现方式。

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

    该Plugin类时Mybatis提供的一个工具类

      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;
      }
    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 {
            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;
      }
    
      private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
        Set<Class<?>> interfaces = new HashSet<>();
        while (type != null) {
          for (Class<?> c : type.getInterfaces()) {
            if (signatureMap.containsKey(c)) {
              interfaces.add(c);
            }
          }
          type = type.getSuperclass();
        }
        return interfaces.toArray(new Class<?>[interfaces.size()]);
      }
    

    可见,Mybatis的插件机制是通过动态代理实现的,且多个插件会层层代理,代理的顺序就是在xml中配置插件的顺序。

    相关文章

      网友评论

          本文标题:原生Mybatis源码简析(下)

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