原生Mybatis源码简析(下)

2019-04-13  本文已影响0人  Hogantry

在上一篇文章中(原生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中配置插件的顺序。

上一篇下一篇

猜你喜欢

热点阅读