天天看点

Mybatis中获取获取代理对象的之后的执行操作Mybatis中获取获取代理对象的之后的执行操作

Mybatis中获取获取代理对象的之后的执行操作

之前已经说了Mybatis中mapper是怎么和XMl关联起来的已经分析了,Mybaits从XML是怎么和Mapper对应上来的。这里从宏观上介绍一下是怎么执行的。之后会在具体的博客里面介绍。

1. 几个重要的步骤

  1. 调用方法的时候调用

    MapperProxy

    invoke

    方法,并且构建

    MapperMethodInvoker

    调用

    invoke

    方法,然后会调用

    MapperMethod

    方法的

    execute

    方法。
  2. 组装参数,将方法的参数组成Map,Map的key是String,Value是具体的参数值(ParamNameResolver)
  3. 调用sqlSession的select方法
  4. 通过StatementId(全限定类名+方法名)从Configuration里面解析

    MappedStatement

    从下面开始,都是在Executor里面运行的。
  5. 通过指定器(Executor)执行

    query

    方法,在这里面构建查询所需要的一切操作。并且执行查询操作,将查询的结果封装实体类,返回
    1. 构建BoundSql对象(通过参数和原始的sql来解析动态sql,生成用于PrepareStatement里面的sql)。
      select * from t_class where id = ? and name in ?
                 
    2. 构建

      CacheKey

      (用作缓存,通过sql,同样的参数,同样的offset或者limit)
    3. 先从二级缓存中查找(如果指定开启)
    4. 再从一级缓存中查找(这肯定是开启的)
    5. 创建

      StatementHandler

      在创建它的时候。会创建

      ParameterHandler

      ResultSetHandler

      ,并且调用插件方法包装。
    6. 调用

      StatementHandler

      prepare

      方法
      1. 将之前

        BoundSql

        对象解析好sql设置给Connection

        (connection.prepareStatement(sql))

      2. 给查询设置设置超时时间和

        fetchSize

      3. 调用

        ParameterHandler

        来给设置此次查询的参数。
      4. 执行查询。
      5. 调用

        ResultSetHandler

        来处理结果映射
    7. 返回结果,并且将结果放在一级缓存里面。
    8. 如果条件满足,也放在二级缓存里面。

    那么下面就简单的来看一下大体的流程吧。之后会围绕具体的方面来分析,流程图就不画了,上面的文字写的也很清楚了。

    测试案例

    @BeforeAll
      static void setup() throws Exception {
        //这里只是为了测试方便,运行了测试的脚本。这里的dataSource和MapperConfig里面的没有关系。
    
        createStudentDataSource(); //这就是获取连接,执行脚本。
        final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
        final Reader reader = Resources.getResourceAsReader(resource);
        // 加载配置文件,解析mapper,mapper里面的每个方法都对应一个MapperStatement。大体就是这样,但是具体的实现就复杂了,比如动态sql,mybatis的配置信息。
        //所有的信息都关联到 SqlSessionFactoryBuilder里面的Configuration实体类。包括mapper,setting,还有代理对象的创建
      sqlMapper = new SqlSessionFactoryBuilder().build(reader);
      }
    
    
    @Test
      public void testShouldSelectClassUsingMapperClass(){
        try(
          SqlSession session = sqlMapper.openSession()
          ){
          ClassMapper mapper = session.getMapper(ClassMapper.class);
          long start = System.currentTimeMillis();
          System.out.println(mapper.listClassOwnerStudentByClassId(1,new String[]{"狗蛋"}));
          System.out.println("start:" + (System.currentTimeMillis()-start));
    
    
          long start1 = System.currentTimeMillis();
          System.out.println(mapper.listClassOwnerStudentByClassId(2,new String[]{"狗蛋"}));
          System.out.println("start1:" + (System.currentTimeMillis()-start1));
    
    
    
          long start2 = System.currentTimeMillis();
          System.out.println(mapper.listClassOwnerStudentByClassId(1,new String[]{"狗蛋"}));
          System.out.println("start2:" + (System.currentTimeMillis()-start2));
    
          session.commit();
        }
      }
               

调用MethodProxy,调用Invoke方法,并且组装参数

// MapperProxy的Invoke方法,这里会构建MapperMethodInvoker大多数的是PlainMethodInvoker。但是对于default修饰的方法
//是 DefaultMethodInvoker,

@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 {
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }



//MapperMethod,通过之前构建的时候确认的类型来判断,这里的模式美其名曰,策略模式
// 这里我用SELECT方法举例,
// 在这个分支里面会根据返回值的类型不同,来约定返回方法分支。
// 下面的代码是按照method.returnsMany()来的。
 public Object execute(SqlSession sqlSession, Object[] args) {
    Object result; //美其名曰,策略模式
    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());
    }
 }

           

组装参数,将方法的参数组成Map,Map的key是String,Value是具体的参数值(ParamNameResolver)

在组装MapperMethod的时候会构建

MethodSignature

,这里面会设置

ParamNameResolver

,在从上面的代码往下走的时候,会通过

ParamNameResolver

来解析参数,即就是组装成map。

// ParamNameResolver中的getNamedParams方法
// 这里的方法能解释在Mapper里面可以用Mybatis提供的默认参数。
public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) { //这里就可以看到,会给参数设置一个默认的值,如果不设置的话,并且还会生成按照下标会同时会生成GENERIC_NAME_PREFIX前缀,同时也会放在map里面返回
      Object value = args[names.firstKey()]; //这里如果只有一个参数,并且它还没有标注param注解,下面的这个方法,就很简单,如果是collection或者array的话,存放默认值,如果不是,就把原来的参数值直接返回
      return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
           

通过StatementId(全限定类名+方法名)从Configuration里面解析

MappedStatement

// DefaultSqlSession中的selectList。
 // 从configuration的MapperStatement通过StatementId获取MapperStatement。
  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try { //拿到mapperStatement,这个是在xml中解析mapper文件的时候解析出来的
      MappedStatement ms = configuration.getMappedStatement(statement);
      
      // 查询交给Executor来执行。
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);// 这里就很离谱,居然还再次包装了一下下。很离谱,对应Collection或者Array
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
           

构建BoundSql对象(通过参数和原始的sql来解析动态sql,生成用于PrepareStatement里面的sql)。

如果开启了二级缓存

DefaultSqlSession

中的

Executor

CachingExecutor

,如果没有开启就是直接是

SimpleExecutor

// ExecutorType有三种,SIMPLE,REUSE,BATCH,一般来说,都是simple。并且它默认的执行器的类型就是simple
  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);
    }

    //如果开启了缓存就用CachingExecutor来包装之前搞好的Executor,
    // CachingExecutor里面有一个TransactionalCacheManager,这是mybatis实现二级缓存的重点。
    
    
    // cacheEnabled这个标识位。就是xml中Setting设置的。,默认是开启,如果开启,就将executor用CachingExecutor包装。
    // 在说一下,executorType默认是SIMPLE,并且这个也是可以通过xml中setting的defaultExecutorType来设置的。
   
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //在interceptorChain应用Executor。
    //waring 这里也很重要,这是是实现mybatis拦截器的主要实现。主要看这个方法,Interceptor类里面的wrap方法
    // 其实这里就是生成了代理对象,调用 Plugin.wrap(target, this);来生成代理对象,在这里会解析interceptor的上面的注解,组成map,key是接口的class。
    // value是set,里面存放的需要拦截的方法。如果需要生成代理对象就生成代理对象,InvocationHandler是Plugin。 具体的还得看看方法里面是怎么写的。
    executor = (Executor) interceptorChain.pluginAll(executor);

    return executor;
  }
           

通过executor来执行方法,在这个方法里面会通过参数来解析动态sql。并且将sql中参数的位置变为

?

占位符。

//MappedStatement的 getBoundSql方法,在这里会将之前搞好的参数(也就是那个Map)传递进来并且根据这些参数,来解析动态sql。
//动态sql的具体实现是 SqlNode。接口
public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }
           

构建

CacheKey

(用作缓存,通过sql,同样的参数,同样的offset或者limit)

构建缓存key,构建这样的key的要求可多了,从代码就可以看出,一个cachekey方方面面那是得完全一样的呀。包括,参数,原始sql,包括sql参数的位置。我意思是比如有这样的一个sql

select * from t_class where a = 1 and b = 2 
select * from t_class where a = 2 and b = 1 
           

这两sql对于我们来说,结果是一样的,但对于Mybatis来说,这可不一样,不能共用一个CacheKey。

话说回来,这确实的一模一样,但凡要是有一点不一样,两次查询,只因为那一点不一样,出来的结果确实一样,那不离谱了吗?

// BaseExecutor的createCacheKey方法
  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();//方法参数的映射
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();//在这里肯定是会用到类型处理的,
    // mimic DefaultParameterHandler logic  这个parameterMappings,按照下标和?对应。下标为0的,表示为sql中第一个问号。
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();//参数名字。
        if (boundSql.hasAdditionalParameter(propertyName)) {
          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);
        }
        cacheKey.update(value);
      }
    } //上面的都是通过propertyName来获取value。
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

           

先从二级缓存中查找(如果指定开启),再从一级查询,并且将结果放在二级和一级缓存中去。

二级缓存相关代码

// CachingExecutor中的query方法

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {

    // 这个缓存是在mapper.xml中配置的。在解析xml文件的时候配置的。
    Cache cache = ms.getCache();

    if (cache != null) {
      flushCacheIfRequired(ms);
      // MapperStatement如果要配置缓存,并且resultHandler为null。就去查二级缓存。
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        
         //从二级缓存中查询,如果没有再从一级缓存里面去查,这里的delegate就是BaseExecutor
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          
          //查到的结果放在二级缓存中
          tcm.putObject(cache, key, list);
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

           

一级缓存相关代码

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) {//lcnote 这个deferredLoads是干嘛的,
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      //如果缓存缓存的作用域是STATEMENT,在查询结束之后会清楚缓存,这肯定是线程不安全的。有问题,如果两个线程同时操作,一个不需要清楚缓存
      // 一个需要清楚缓存,那不就得么的了。出现问题了?
      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; 
    // 在一级缓存中存放执行占位符,
    //为什么要这样做呢?如果一个
   // 如果先一个查询,后一个再来,正好中间有一个key的话。就拿到脏的数据了。也没看到他对这个数据有什么特殊的处理呀
    
    localCache.putObject(key, EXECUTION_PLACEHOLDER); 
    try {
      // 查询数据库
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      // 移除掉之前用于占位的key
     // 这里的remove我不是很理解,与其说是占位,name在这里直接put覆盖就好了,这里这样做怕是为了防止doQuery发生异常,导致这个缓存是错误的,所以, 这里必须有finally,这都是细节细节
      localCache.removeObject(key); 
    }
    
    
    localCache.putObject(key, list);//放在一级缓存中。

    
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

           

创建

StatementHandler

在创建它的时候。会创建

ParameterHandler

ResultSetHandler

,并且调用插件方法包装

创建StatementHandler,并且用interceptor包装他,关于interceptor在前面的博客中已经介绍了。

//Configuration的newStatementHandler方法
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;
  }
           

创建parameterHandler和resultSetHandler并且interceptor包装

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);
  }
           

得到Connection并且设置sql,和设置此次查询的一些基本属性

// 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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);//查询并且映射结果集
    } finally {
      closeStatement(stmt);
    }
  }
           

设置sql,设置属性

//BaseStatementHandler的prepare方法 
@Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //实例化和初始化,这里会调用Connection的 connection.prepareStatement(sql)方法,设置原始的sql
      statement = instantiateStatement(connection);
      //设置statement属性
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
           

调用

ParameterHandler

来给设置此次查询的参数。

// DefaultParameterHandler的方法,
public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
   //得到参数的映射 ,这里会解析参数的类型,将实际参数的类型转变为JDBC认可的类型。
  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);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            
            //在上面解析完参数并且确定好类型之后,这里会通过实际的typeHandler来PreparedStatement对应的位置设置参数
           //注意这里的+1操作。原始的JDBC里面设置参数是从下标1开始的。之前已经将需要查询的sql已经设置进去了。
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }
           

执行查询并且返回结果

执行查询

//PreparedStatementHandler方法
@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //waring 这里会真正的执行sql,下面就开始处理结果集
    ps.execute();
    
    //处理结果集映射
    return resultSetHandler.handleResultSets(ps);
  }
           

返回结果,处理结果集映射。

关于这里的代码,我没有详细的看过,之后看完之后会有详细分析一下。

@Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
   //这个集合就是用来存放sql查询结果之后,封装好的对象。
    final List<Object> multipleResults = new ArrayList<>();
    //将结果包装成ResultSetWrapper,统计了每一列的类型和名字,还有对应的jdbc的数据类型
    int resultSetCount = 0;

    // 将 statement的ResultSet包装为ResultSetWrapper。
    ResultSetWrapper rsw = getFirstResultSet(stmt);
  
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    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); //将上面的改变,合并为一个单一的list。
  }
           

Mybatis中获取获取代理对象的之后的执行操作就分析到这里了。如有不正确的地方,欢迎指出。谢谢。