Mybatis源码流程剖析

一、Mybatis初始化流程

1.我们如何初始化Mybatis

InputStream inputStream = Resources.getResourcesAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputstream);

2.分析源码

首先我们找到SqlSessionFactoryBuilder类的build方法(这里使用了建造者模式)

public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
}

这里调用了另一个重载方法

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      // XMLConfigBuilder 是专门解析mybatis的配置文件的类
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      // parser.parse()的返回值是一个Configuration对象
      // 然后调用了一个新的重载方法
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

Mybatis在初始化的时候,会将Mybatis的配置信息全部加载到内存中,使用Configuration实例来维护。Configuration对象的结构与xml文件的配置几乎一样,初始化配置文件的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部的属性中。
接下来我们需要研究下parser.parse()是怎么获得Configuration对象的

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 解析 XML Configuration 节点
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      // 解析<properties />标签
      propertiesElement(root.evalNode("properties"));
      // 解析<settings />标签
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      // 加载自定义的VFS实现类
      loadCustomVfs(settings);
      // 加载logImpl实现类
      loadCustomLogImpl(settings);
      // 解析 <typeAlias />标签
      typeAliasesElement(root.evalNode("typeAliases"));
      // 解析<plugins />标签
      pluginElement(root.evalNode("plugins"));
      // 解析<objectFactory />标签
      objectFactoryElement(root.evalNode("objectFactory"));
      // 解析<objectWrapperFactory />标签
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      // 解析<reflectorFactory />标签
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      // 赋值<settings />标签至configuration属性
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      // 解析<environments />标签
      environmentsElement(root.evalNode("environments"));
      // 解析<databaseIdProvider />标签
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // 解析<typeHandlers />标签
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 解析<mappers />标签
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

在上面的代码中我们需要注意解析<mappers >标签的语句。mappers标签用来引用mappers.xml文件或者mapper接口的目录。
如这样的mapper.xml,这样的一个select标签会在初始化配置文件时解析封装成一个MapperStatement对象,然后存储在Configuration对象的mappedStatements属性中。mapperdStatements是一个HashMap,key=全限定类名+方法名,value=对应的MapperStatement对象

<select id="getUser" resultType="user" >
  select * from user where id=#{id}
</select>

在Configuration类中对应的属性:

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
      .conflictMessageProducer((savedValue, targetValue) ->
          ". please check " + savedValue.getResource() + " and " + targetValue.getResource());

到此,xml文件的解析就结束了,然后会回调build方法,返回一个SqlSessionFactory的默认实现对象(这个对象会携带刚刚解析完成的configuration对象)

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

二、SqlSession执行SQL流程

1.如何使用SqlSessionFactory执行SQL语句

Sqlsession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("com.example.mapper.UserMapper.getUserByName");

2.源码分析

首先,我们需要了解SqlSession。它是一个接口,有两个的实现类:DefaultSqlSession(默认)和SqlSessionManager(弃用)。
SqlSession是Mybatis中用于和数据库交互的顶层类,通常与ThreadLocal绑定,一个会话使用一个sqlSession,使用完成后需要close。
在SqlSession中最重要的两个参数,一个是configuration,一个是executor

public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
  private final Executor executor;

  private final boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public DefaultSqlSession(Configuration configuration, Executor executor) {
    this(configuration, executor, false);
  }

Executor作为执行器的接口,有三个常用的实现类:
BatchExecutor:重用语句并执行批量更新
ReuseExecutor:重用预处理语句 prepared statements
SimpleExecutor:普通执行器,默认

我们从factory.openSession()的源代码开始分析

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

这里调用了一个重构方法


  /**
  * ExecutorType Executor的类型
  * TransactionIsolationLevel 事务的隔离级别
  * autoCommit 是否开启事务
  */
  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);
      // 根据参数创建指定的executor
      final Executor executor = configuration.newExecutor(tx, execType);
      // 最终返回的是DefaultSqlSession
      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();
    }
  }

然后再看sqlSession.selectList的源代码

  @Override
  public <E> List<E> selectList(String statement) {
    return this.selectList(statement, null);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 根据传入的全限定名+方法名从configuration中取出MappedStatement对象
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 调用Executor中的方法处理,其中rowBounds是用来逻辑分页的,warpCollection(parameter)是用来装饰集合或者数组参数的
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

后续的步骤就是Executor的执行

三、Executor执行流程

进入executor.query()

  /**
  * 此方法在SimpleExecutor的父类BaseExecutor中
  */
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 根据传入的参数动态获得SQL语句,最后生成一个BoundSql对象
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 为本次查询创建缓存的key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    // 调用重载方法
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        // 如果缓存里面没有,就从数据库中查询
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
  
  /**
  * 从数据库查询
  */
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 最终调用doQuery()方法
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    // 将查询的结果放入缓存
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

  /**
  * SimpleExecutor实现父类的doQuery()的抽象方法
  */
  @Override
  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对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 创建jdbc中的statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 调用StatementHandler对象中的query方法进行处理
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  
  /**
  * 创建statement对象的方法
  */
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // getConnection方法经过重重调用,最终会走到openConnection()方法中
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

  protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    // 从连接池获取连接
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }

综述,executor对象的query()方法,几经波折最后会创建一个StatementHandler对象,并将必要的参数传递给statementHandler,使用statementHandler来完成数据库的查询,最终返回一个List结果集。
Executor组件的作用:

  • 根据传递的参数,完成SQL的动态解析,生成BoundSql对象,供StatementHandler使用
  • 为查询创建缓存,已提高性能
  • 创建JDBC的statement连接对象,传递给StatementHandler对象,返回List查询结果

四、StatementHandler执行流程

StatementHandler对象/组件主要完成三件事:

  • JDBC的preparedStatement类型的对象,创建的过程中我们使用的SQL字符串会包含若干个?占位符,我们需要对占位符进行设置值。StatementHandler通过parameterize(statement)方法对statement进行设值(这一步在创建statement对象的方法中已经执行完了)
  • 执行sql
  • StatementHandler通过List query(Statement statement ,ResuletHandler resuletHandler)方法来完成执行Statement和将Statement对象返回的resultSet封装成List

首先我们看handler.parameterize(stmt)的源码

  /**
  * PreparedStatementHandler实现StatementHandler接口的方法
  */
  @Override
  public void parameterize(Statement statement) throws SQLException {
    // 使用parameterHandler对象来完成对statement的设值
    parameterHandler.setParameters((PreparedStatement) statement);
  }
  /**
  * ParameterHandler实现StatementHandler接口的方法,用来对某一个Statement进行设置参数
  */
  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          // 每一个mapping都有一个TypeHandler,根据TypeHandler对preparedStatement进行参数设置
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 设置参数
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

从上面的代码中我们知晓,handler.parameterize(stmt)调用了parameterHandler.setParameters((PreparedStatement) statement)
setParameters方法根据我们输入的参数,对statement对象的占位符进行赋值(这里面涉及到TypeHandler对象)

StatementHandler的第二步会调用statement.execute(sql)方法执行sql,第三步会调用query(Statement statement ,ResuletHandler resuletHandler)方法将statement对象执行后的ResultSet结果集转换成List结果集,源码如下:

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    // 使用ResultSetHandler来封装处理execute的ResultSet方法
    return resultSetHandler.handleResultSets(statement);
  }

  //
  // HANDLE RESULT SETS
  //
  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    // 每个ResultSet对应一个Object对象,实际上每个Object是一个List<Object>对象
    // 在不考虑存储过程的多ResultSet的情况,普通的查询,就会有一个ResultSet,也就是说最后生成的multiplesResults最多就一个一个元素
    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 1.通过getFirstResultSet()方法获得ResultSet的包装类ResultSetWrapper对象。其实就是首个ResultSet对象
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 3.获得ResultMaps数组(在不考虑存储过程的多ResultSet情况,普通的查询,就一个ResultSet,也就是说resultMaps就一个元素)
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      // 获得第i个ResultMap对象
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 实际处理ResultSet的方法,将结果添加到multipleResults里面
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 2.通过getNextResultSet()方法,获得下一个ResultSet对象,并封装成ResultSetWrapper对象
      rsw = getNextResultSet(stmt);
      // 清理
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    
    // mappedStatement.resultSets只在存储过程中使用,所以下面的代码可以忽略
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

五、Mapper代理方式

实际上,我们在使用Mybatis执行sql时,一般不会使用sqlSession.selectList()的方法,而是采用代理的方式,如下:

  public static void main(String[] args) {
    //前三步都相同
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = factory.openSession();
    //这里不再调用SqlSession的api,而是获得了接口对象,调用接口中的方法。
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> list = mapper.getUserByName("tom");
}

但是,我们并没有实现UserMapper,为什么可以直接调用呢?答案很简单:动态代理技术
补充一个细节,Mybatis初始化时产生的Configuration对象中有一个属性叫MapperRegistry,它内部维护了一个HashMap,用于mapper接口的工厂类。

<mappers>
  <mapper class="com.lagou.mapper.UserMapper"/>
  <package name="com.lagou.mapper"/>
</mappers>

当解析mappers标签时

  • 如果解析到的是mapper配置文件,它会将配置文件中的增删改查标签封装成MappedStatement对象,存入mappedStatements中,然后放入到Configuration中
  • 如果解析到的是接口,会创建此接口对应的MapperProxyFactory对象,存入到HashMap中,key为此接口的Class对象,value为此接口对应的MapperProxyFactory对象

1.getMapper()方法执行流程

查看sqlSession.getMapper(UserMapper.class )的源码

  @Override
  public <T> T getMapper(Class<T> type) {
    // 调用configuration的getMapper()方法
    return configuration.getMapper(type, this);
  }

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从mapperRegistry中获得代理对象
    return mapperRegistry.getMapper(type, sqlSession);
  }

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从MapperRegistry中的HashMap中获得MapperProxyFactory
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      // 调用工厂的newInstance方法获得结果
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

  public T newInstance(SqlSession sqlSession) {
    // 创建一个MapperProxy对象
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    // 调用重构方法
    return newInstance(mapperProxy);
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 使用JDK的动态代理技术获得代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

使用JDK的动态代理技术获得代理对象,它意味着jdk动态代理的InvocationHandler接口的实现在mapperProxy中。继续翻阅MapperProxy源码

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -4724728412955527868L;
  private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
      | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
  private static final Constructor<Lookup> lookupConstructor;
  private static final Method privateLookupInMethod;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache;

  //构造,传入了 SqlSession,说明每个session中的代理对象的不同的!
  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  static {
    Method privateLookupIn;
    try {
      privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
    } catch (NoSuchMethodException e) {
      privateLookupIn = null;
    }
    privateLookupInMethod = privateLookupIn;

    Constructor<Lookup> lookup = null;
    if (privateLookupInMethod == null) {
      // JDK 1.8
      try {
        lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
        lookup.setAccessible(true);
      } catch (NoSuchMethodException e) {
        throw new IllegalStateException(
            "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
            e);
      } catch (Exception e) {
        lookup = null;
      }
    }
    lookupConstructor = lookup;
  }
  
  // invoke方法的具体细节
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
...

}

2.invoke()方法执行流程

我们知道,在动态代理返回了示例后,我们就可以直接调用mapper类中的方法了,但代理对象调用方法,执行是在MapperProxy中的invoke方法中

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {  // 如果是Object定义的方法,直接调用
        return method.invoke(this, args);
      } else {
        // 获得 MapperMethodInvoke 对象,重点在这:MapperMethodInvoke最终调用了method对象的invoke方法。
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  

  /**
  * 
  * Method的具体实现方法
  */
  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      // A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
      // It should be removed once the fix is backported to Java 8 or
      // MyBatis drops Java 8 support. See gh-1929
      MapperMethodInvoker invoker = methodCache.get(method);
      if (invoker != null) {
        return invoker;
      }

      return methodCache.computeIfAbsent(method, m -> {
        if (m.isDefault()) {
          // 如果是默认的标准方法
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          // 不是的化,构造一个PlainMethodInvoke对象
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }

    /**
     * Returns {@code true} if this method is a default
     * method; returns {@code false} otherwise.
     *
     * A default method is a public non-abstract instance method, that
     * is, a non-static method with a body, declared in an interface
     * type.
     *
     * @return true if and only if this method is a default
     * method as defined by the Java Language Specification.
     * @since 1.8
     */
    public boolean isDefault() {
        // Default methods are public non-abstract instance methods
        // declared in an interface.
        return ((getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) ==
                Modifier.PUBLIC) && getDeclaringClass().isInterface();
    }


这里需要查看下PlainMethodInvoker,它是一个内部类,实现了MapperMethodInvoker接口,最终的最终会调用到mapperMethod.execute(sqlSession, args)

private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {
      super();
      this.mapperMethod = mapperMethod;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      //实际上还是走到了MapperMethod的execute方法
      return mapperMethod.execute(sqlSession, args);
    }
  }

进入execute方法

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 判断mapper中的方法类型,最终调用的还是SqlSession中的方法
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

至此,我们常用的Mapper方式的源码追踪完。

posted @ 2021-03-11 20:26  寒天峰  阅读(182)  评论(0编辑  收藏  举报