MyBatis 源碼分析篇 4:Mapper 方法執(zhí)行

通過上一篇文章 MyBatis 源碼分析篇 3:getMapper 我們已經(jīng)知道 MyBatis 通過動(dòng)態(tài)代理的方式獲取 Mapper 實(shí)例。在這一篇文章中我們就來具體討論其動(dòng)態(tài)代理的具體實(shí)現(xiàn)和 Mapper 中方法是如何執(zhí)行的。

以下面代碼為入口,我們來進(jìn)行 debug 代碼跟入:

List<Author> author = mapper.selectAllTest();

selectAllTest 方法的 sql 如下:

select id, name, sex, phone from author

不出意外的我們會(huì)首先進(jìn)入代理類 org.apache.ibatis.binding.MapperProxy 的 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 if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

首先我們需要注意一下該類,該類持有一個(gè) SqlSession 類型的成員變量,并在構(gòu)造方法中進(jìn)行了賦值:

  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

這也完全在我們意料之中,不知道大家還記不記得我們在 MyBatis 源碼分析篇 2:SqlSession 中討論的,SqlSession 包含了執(zhí)行數(shù)據(jù)庫操作的所有方法。那么我們反過來想一下,MapperProxy 代理類是要對我們在 Mapper 中聲明的方法進(jìn)行執(zhí)行,而數(shù)據(jù)庫操作的方法都在 SqlSession 中,那么它就一定要持有一個(gè) SqlSession,來調(diào)用 SqlSession 中對應(yīng)的方法!

接下來我們就來驗(yàn)證一下我們的猜想。我們再次回到 MapperProxy 的 invoke 方法。注意最后一行代碼:return mapperMethod.execute(sqlSession, args);,跟入該行代碼,會(huì)進(jìn)入 org.apache.ibatis.binding.MapperMethod 類的 execute(SqlSession sqlSession, Object[] args) 方法:

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      //其余類型略...
      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);
        }
        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;
  }

在上面的代碼中,因?yàn)槲覀儨y試執(zhí)行的是返回值為 List 類型的 select 方法,那么接著會(huì)進(jìn)入第 10 行代碼:result = executeForMany(sqlSession, args); 其實(shí)現(xiàn)為:

  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.<E>selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

因?yàn)槲覀儾]有綁定 RowBounds,那么會(huì)執(zhí)行第 8 行代碼:result = sqlSession.<E>selectList(command.getName(), param);。果然,到這一步就驗(yàn)證了我們之前的猜想,它果然是調(diào)用了 SqlSession 的 selectList 方法!

至此,代碼執(zhí)行就回到了 SqlSession 的 selectList,我們在之后的文章 MyBatis 源碼分析篇 5:Mapper 方法執(zhí)行之 Executor 詳細(xì)討論其底層實(shí)現(xiàn)。

附:

當(dāng)前版本:mybatis-3.5.0
官網(wǎng)文檔:MyBatis
項(xiàng)目實(shí)踐:MyBatis Learn
手寫源碼:MyBatis 簡易實(shí)現(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容