【Mybatis-源码解析】1.2 启动过程中,Mapper.xml的解析

621 阅读14分钟

1.2 启动过程中,Mapper.xml的解析

整体流程

简易流程图

graph LR
整体流程 --> 解析引用类缓存 --> 解析当前类缓存 --> 扫描器进行扫描 --> 
解析入参映射关系 --> 解析出参映射关系 --> 解析SQL代码块 --> 解析SQL执行语句 --> 解析SQL节点 --> 结束

文本描述

  • 启动时会初始化XMLMapperBuilder对象
    • 内部构建XPathParser
  • 进行解析
    • 解析引用类缓存(cache-ref节点) cacheRefElement
    • 解析当前类缓存(cache节点) cacheElement
      • Configuration在构建过程中会在typeAliasRegistry的TYPE_ALIASES赋值
    • 解析入参映射关系(/mapper/parameterMap 节点)parameterMapElement
    • 解析出参映射关系(/mapper/resultMap 节点)resultMapElements
      • 处理构造函数节点
      • 构建ResultMapping属性
      • 解析discriminator(鉴频器)
    • 解析SQL代码块(/mapper/sql 节点) sqlElement
      • 解析SQL执行语句() buildStatementFromContext 解析
    • 解析SQL节点(select|insert|update|delete节点
      • 解析SelectKeyNodes processSelectKeyNodes

父类接口 BaseBuilder

其他类解析:XPathParser

XML中的使用 XPathParser 进行解析

初始化

  // 启动时通过此方法构建
  public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, 
        resource, 
        sqlFragments);
  }
  
  // 核心初始化方法 设置父类的configuration、根据配置和资源构建builder助理、设置parser(解析器)、设置sql片段、设置资源
  private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }

核心方法 parse(解析)

  public void parse() {
    // 判断资源是否被加载(判断configuration.loadedResources是否有该资源),如果没有加载,进行加载
    if (!configuration.isResourceLoaded(resource)) {
      // 解析 /mapper 元素 通过xpath解析节点
      configurationElement(parser.evalNode("/mapper"));
      
      // 将该资源记载到 configuration.loadedResources中,标记为已加载
      configuration.addLoadedResource(resource);
      
      /**
       * 根据 Namespace 构建Mapper
       * 1. 获取到解析出来的 Namespace 如果 Namespace 不为空 加载该接口
       * 2. 判断 configuration.knownMappers 是否有该接口类
       * 3. 如果没有在  configuration.loadedResources 中添加 "namespace:" + namespace 类,并将 configuration.knownMappers 添加 该接口类
       */
      bindMapperForNamespace();
    }
   
    // 获取IncompleteResultMap中在解析过程中失败的ResultMap,再重新进行加载
    parsePendingResultMaps();
    
    // 获取IncompleteCacheRef中在解析过程中失败的CacheRef,再重新进行加载
    parsePendingCacheRefs();
    
    // 获取IncompleteStatement中在解析过程中失败的Statement,再重新进行加载
    parsePendingStatements();
  }

解析元素 configurationElement

如果Config中没有解析过该文件

  private void configurationElement(XNode context) {
    try {
      // 在节点中获取namespace属性 获取到映射的mapper接口类
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      
      // 构建器中获取到映射属性
      builderAssistant.setCurrentNamespace(namespace);
      
      // 在mapper节点中 获取到cache-ref节点 用于配置引用类缓存
      cacheRefElement(context.evalNode("cache-ref"));
      
      // 在mapper节点中 获取cache节点 用于配置当前类缓存
      cacheElement(context.evalNode("cache"));
      
      // 在mapper节点中 获取 /mapper/parameterMap 节点 设置参数映射
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      
      // 在mapper节点中 获取 /mapper/resultMap 节点 设置返回结果
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      
      // 在mapper节点中 获取 /mapper/sql 节点
      sqlElement(context.evalNodes("/mapper/sql"));
      
      // 解析sql语句
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

解析引用类缓存(cache-ref节点) cacheRefElement

在当前mapper执行过程中,如果配置了CacheRef,那么CacheRef中配置的接口类,将在当前类使用过程中使用缓存,首先请确保CacheRef中配置的接口类已经使用了缓存

  private void cacheRefElement(XNode context) {
    // 如果节点不为空
    if (context != null) {
      // 配置中添加缓存信息(添加到cacheRefMap中,KEY为当前的Mapper接口全限定名,value为需要缓存的权限限定名)
      configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
      
      // 构建CacheRefResolver
      CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
      try {
        // 根据缓存的namespace获取缓存对象,并设置当前缓存currentCache
        cacheRefResolver.resolveCacheRef();
      } catch (IncompleteElementException e) {
        // 如果解析失败,存储到IncompleteCacheRef中,在处理完后加载
        configuration.addIncompleteCacheRef(cacheRefResolver);
      }
    }
  }

解析当前类缓存(cache节点) cacheElement

  private void cacheElement(XNode context) throws Exception {
    // 如果节点不为空
    if (context != null) {
      // 在节点中获取type属性,如果type属性为空,那么设置的默认值为:PERPETUAL
      String type = context.getStringAttribute("type", "PERPETUAL");
      
      // 根据缓存类型注册缓存获取缓存类
      /**
        * 在typeAliasRegistry.TYPE_ALIASES中根据类型获取
        * 如果获取不到,通过Resources.classForName加载传入的类名
        */
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      
      // 在节点中获取eviction属性,如果eviction属性为空,name设置默认值为:LRU
      String eviction = context.getStringAttribute("eviction", "LRU");
      
      // 根据eviction类型加载eviction类
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      
      // 在节点中获取flushInterval属性
      Long flushInterval = context.getLongAttribute("flushInterval");
      // 在节点中获取size属性
      Integer size = context.getIntAttribute("size");
      // 在节点中获取是否只读标识,默认为false
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      // 在节点中获取是否阻塞标识,默认为false
      boolean blocking = context.getBooleanAttribute("blocking", false);
      // 在节点中获取子节点 构建成props对象
      Properties props = context.getChildrenAsProperties();
      // 生成代理器,使用缓存
      /**
        * 构建缓存对象:设置缓存类、缓存的回收策略、缓存刷新间隔、缓存大小、读写状态、阻塞状态、扩展属性
        * 并在configuration中设置缓存对象
        * 设置当前类的缓存对象
        */
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }
Configuration在构建过程中会在typeAliasRegistry的TYPE_ALIASES赋值
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
缓存的回收策略
  • LRU:最近最少使用,移除最长时间不被使用的对象
  • FIFO:先进先出,按对象进入缓存的顺序来移除它们
  • SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
  • WEAK:弱引用,更积极地移除基于垃圾收集器和弱引用规则的对象

解析入参映射关系(/mapper/parameterMap 节点)parameterMapElement

private void parameterMapElement(List<XNode> list) throws Exception {
    // 遍历parameterMap节点
    for (XNode parameterMapNode : list) {
      // 获取节点中的 ID 属性
      String id = parameterMapNode.getStringAttribute("id");
      
      // 获取节点中的 type 属性
      String type = parameterMapNode.getStringAttribute("type");
      
      // 加载 type类
      Class<?> parameterClass = resolveClass(type);
      
      // 在parameterMap节点中获取到 parameter节点
      List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
      
      // 声明参数参数的映射关系
      List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
      
      // 遍历parameter节点
      for (XNode parameterNode : parameterNodes) {
        // 在parameter节点中 获取 property、javaType、jdbcType、resultMap、mode、typeHandler、numericScale 属性
        /**
         * property 类中的属性名
         * javaType java类型
         * jdbcType 数据库类型
         * resultMap 结果映射关系
         * mode 标识参数类型:IN - 入参,OUT - 出参,INOUT - 入参和出参
         * typeHandler 类型处理器
         * numericScale 小数精度
         */
        String property = parameterNode.getStringAttribute("property");
        String javaType = parameterNode.getStringAttribute("javaType");
        String jdbcType = parameterNode.getStringAttribute("jdbcType");
        String resultMap = parameterNode.getStringAttribute("resultMap");
        String mode = parameterNode.getStringAttribute("mode");
        String typeHandler = parameterNode.getStringAttribute("typeHandler");
        Integer numericScale = parameterNode.getIntAttribute("numericScale");
        
        // 加载参数类型枚举
        ParameterMode modeEnum = resolveParameterMode(mode);
        
        // 加载java类型类
        Class<?> javaTypeClass = resolveClass(javaType);
        // 获取到jdbc类型枚举
        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
        
        // 加载 类型处理器
        Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
        
        // 根据加载好的参数 构建参数映射
        /**
         * 1. 解析resultMap (拼接包名)
         * 2. 解析javaTypeClass,如果javaType为空那么:判断JdbcType为CURSOR时加载java.sql.ResultSet类,如果返回结果为Map时加载object类,如果获取不到加载Object类型
         * 3. 解析类型处理器
         * 4. 构建ParameterMapping对象
         */
        ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
        
        // 将结果对象添加到parameterMappings中
        parameterMappings.add(parameterMapping);
      }
      
      // 构建代理添加 configuration.parameterMaps对象中(KEY 为 id,value为构建好的ParameterMap对象)
      builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
    }
  }

解析出参映射关系(/mapper/resultMap 节点)resultMapElements

遍历所有的resultMap节点 调用resultMapElement方法

  private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    // 在ThreadLocal中记录
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    
    // 在 节点中获取 id属性
    String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
    
    // 在 节点中获取 type类型,如果type为空获取ofType、如果ofType为空获取resultType、如果resultType为空获取javaType
    String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType"))));
                
    // 在 节点中获取 extend属性 
    String extend = resultMapNode.getStringAttribute("extends");
    // 在 节点中获取 autoMapping属性
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    
    // 加载resultMap的类
    Class<?> typeClass = resolveClass(type);
    
    // 声明 discriminator(鉴频器)
    Discriminator discriminator = null;
    // 声明 resultMappings
    List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
    // 添加原有的resultMappings
    resultMappings.addAll(additionalResultMappings);
    
    // 在resultMap节点中获取子节点
    List<XNode> resultChildren = resultMapNode.getChildren();
    
    // 遍历所有的 resultMap节点
    for (XNode resultChild : resultChildren) {
      // 如果节点名为 构造函数 节点
      if ("constructor".equals(resultChild.getName())) {
        /**
         * 1. 获取constructor节点的子节点
         * 2. 添加flags中添加构造函数,如果节点名为idArg那么添加id 
         * 3. 构建ResultMapping 并将结果添加到resultMappings中
         */
        processConstructorElement(resultChild, typeClass, resultMappings);
        
      } 
      // 如果节点名为 discriminator(鉴频器)
      else if ("discriminator".equals(resultChild.getName())) {
        // 构建 discriminator(鉴频器)
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
        // 判断节点名字是否为id 如果为ID 在标志中添加id字段
        List<ResultFlag> flags = new ArrayList<ResultFlag>();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        // 构建ResultMapping 并将结果添加到resultMappings中
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    
    // 根据上述属性构建 resultMapResolver
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      /**
       * 1. 如果当前resultMap设置了集成属性,在configuration中获取继承的ResultMap,并取 继承的和本身的并集
       * 2. 将组装好的ResultMap存储在configuration的resultMaps中 key 为 设置的ID value 为构建好的映射关系合集
       */
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      // 如果解析异常了,存放到IncompleteResultMap中,在处理结束后再次加载
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }
处理构造函数节点
  private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
    List<XNode> argChildren = resultChild.getChildren();
    for (XNode argChild : argChildren) {
      List<ResultFlag> flags = new ArrayList<ResultFlag>();
      flags.add(ResultFlag.CONSTRUCTOR);
      if ("idArg".equals(argChild.getName())) {
        flags.add(ResultFlag.ID);
      }
      resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
    }
  }
构建ResultMapping属性
  private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
    String property;
    // 如果存在构造函数标志 property为节点中的name属性 否则 为节点的property属性
    if (flags.contains(ResultFlag.CONSTRUCTOR)) {
      property = context.getStringAttribute("name");
    } else {
      property = context.getStringAttribute("property");
    }
    
    // 在节点中获取 column 列名
    String column = context.getStringAttribute("column");
    // 在节点中获取 javaType java类型
    String javaType = context.getStringAttribute("javaType");
    // 在节点中获取 jdbcType 数据库类型
    String jdbcType = context.getStringAttribute("jdbcType");
    
    // 在节点中获取 select 用于执行对应的select语句
    String nestedSelect = context.getStringAttribute("select");
    
    // 在节点中获取 resultMap 
    String nestedResultMap = context.getStringAttribute("resultMap",
        processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
    
    // 在节点中获取 notNullColumn 不为空的列 (如果该列为空,不生成子对象 - 待认证
    String notNullColumn = context.getStringAttribute("notNullColumn");
    
    // 在节点中获取 columnPrefix 当有多个结果集时,用该字段区分
    String columnPrefix = context.getStringAttribute("columnPrefix");
    
    // 在节点中获取 typeHandler 类型处理器
    String typeHandler = context.getStringAttribute("typeHandler");
    
    // 在节点中获取 resultSet 当有多个结果集时 获取设置的结果集
    String resultSet = context.getStringAttribute("resultSet");
    // 在节点中获取 foreignColumn 当有多个结果集时 映射字段
    String foreignColumn = context.getStringAttribute("foreignColumn");
    
    // 在节点中获取 fetchType 默认值为 在配置中获取的是否启动懒加载,如果启动返回TRUE 否则返回 FALSE
    boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
    
    // 加载 java类
    Class<?> javaTypeClass = resolveClass(javaType);
    
    // 加载 类型处理器类
    Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
    
    // 解析 jdbc类型
    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    
    // 构建代理 根据配置 构建ResultMapping对象返回
    return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
  }
解析discriminator(鉴频器)
  private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
    // 在节点中获取到 column 列名
    String column = context.getStringAttribute("column");
    // 在节点中获取到 javaType java类型
    String javaType = context.getStringAttribute("javaType");
    // 在节点中获取到 jdbcType 数据库类型
    String jdbcType = context.getStringAttribute("jdbcType");
    // 在节点中获取到 typeHandler 类型处理器
    String typeHandler = context.getStringAttribute("typeHandler");
    
    // 解析java类
    Class<?> javaTypeClass = resolveClass(javaType);
    // 解析类型处理器
    Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
    // 解析数据库类型
    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    
    // 声明discriminator(鉴频器)Map
    Map<String, String> discriminatorMap = new HashMap<String, String>();
    // 遍历所有的子节点
    for (XNode caseChild : context.getChildren()) {
      // 获取子节点的 value 属性
      String value = caseChild.getStringAttribute("value");
      // 获取子节点的 resultMap 属性
      String resultMap = ceChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings));
      // 将value 和 resultMap 放置到discriminatorMap中
      discriminatorMap.put(value, resultMap);
    }
    // 构建 discriminator(鉴频器)
    return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
  }

解析SQL代码块(/mapper/sql 节点) sqlElement

  private void sqlElement(List<XNode> list) throws Exception {
    // 如果 数据库配置了 DatabaseId(数据库厂商 方言)使用方言解析sql 额否则直接解析
    if (configuration.getDatabaseId() != null) {
      sqlElement(list, configuration.getDatabaseId());
    }
    sqlElement(list, null);
  }
  
  private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
    // 遍历所有的sql节点
    for (XNode context : list) {
      // 在节点中获取 databaseId 数据库方言
      String databaseId = context.getStringAttribute("databaseId");
      // 在节点中获取 id 属性
      String id = context.getStringAttribute("id");
      // 组装当前的命名空间 将 id 和当前类锁定
      id = builderAssistant.applyCurrentNamespace(id, false);
      // 判断 databaseId (数据库方言) 是否匹配 如果匹配在sqlFragments添加记录 (同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃)
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        sqlFragments.put(id, context);
      }
    }
  }
解析SQL执行语句() buildStatementFromContext 解析
  private void buildStatementFromContext(List<XNode> list) {
    // 如果 数据库配置了 DatabaseId(数据库厂商 方言)使用方言解析sql 额否则直接解析
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }
  
  // 解析 SQL 语句
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    // 遍历所有节点
    for (XNode context : list) {
      // 创建一个xml构建器
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        // 使用构建器解析
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        // 如果解析失败,添加到IncompleteStatement中,等当前文件解析完后重新构建
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }
  
  

解析SQL节点(select|insert|update|delete节点)XMLStatementBuilder.parseStatementNode

使用XMLStatementBuilder解析

  public void parseStatementNode() {
    // sql节点中 获取 id
    String id = context.getStringAttribute("id");
    // sql节点中 获取 数据库方言
    String databaseId = context.getStringAttribute("databaseId");

    // 如果数据库方言不匹配直接返回
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    // sql节点中 获取 fetchSize
    Integer fetchSize = context.getIntAttribute("fetchSize");
    // sql节点中 获取 timeout 超时时间
    Integer timeout = context.getIntAttribute("timeout");
    // sql节点中 获取 parameterMap 参数映射
    String parameterMap = context.getStringAttribute("parameterMap");
    // sql节点中 获取 parameterType 参数类型
    String parameterType = context.getStringAttribute("parameterType");
    
    // 解析参数类型
    Class<?> parameterTypeClass = resolveClass(parameterType);
    
    // sql节点中 获取 resultMap 结果映射
    String resultMap = context.getStringAttribute("resultMap");
    // sql节点中 获取 resultType 结果类型
    String resultType = context.getStringAttribute("resultType");
    
    // sql节点中 获取 lang 为特定的语句指定语言(驱动类)?
    String lang = context.getStringAttribute("lang");
    // 解析语言驱动
    LanguageDriver langDriver = getLanguageDriver(lang);

    // 解析结果类型
    Class<?> resultTypeClass = resolveClass(resultType);
    
    // sql节点中 获取 resultSetType 指定的结果类型
    String resultSetType = context.getStringAttribute("resultSetType");
    
    // sql节点中 获取并解析 statementType  语句类型
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    // 解析 结果类型
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    // 获取节点的名字
    String nodeName = context.getNode().getNodeName();
    // 获取节点的SQL语句类型
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    
    // 如果为查询语句,那么设置标记
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    
    // sql节点中 获取 flushCache  是否刷新缓存,默认不是查询都会开启
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    
    // sql节点中 获取 useCache  是否使用缓存,默认是查询都会开启
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    
    
    // sql节点中 获取 resultOrdered  是否返回多个结果集,默认是不开启
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // 解析 include 节点
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // 解析 SelectKey 节点
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    
    // langDriver(语言驱动)创建SQL原数据 (设置不同节点的节点处理器)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    
    
    // sql节点中 获取 resultSets  返回结果的集合名字
    String resultSets = context.getStringAttribute("resultSets");
    // sql节点中 获取 keyProperty  返回后设置的属性名
    String keyProperty = context.getStringAttribute("keyProperty");
    // sql节点中 获取 keyColumn  返回后设置的属性的列
    String keyColumn = context.getStringAttribute("keyColumn");
    
    // 声明Key生成器
    KeyGenerator keyGenerator;
    // 声明 keyStatementId ? 为当前SQL语句的ID + !selectKey
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    // 构建当前类的ID
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    
    // 如果配置有ID生成器 通过ID生成器获取keyGenerator 否则 在节点中获取 useGeneratedKeys 使用的KEY生成器
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }

    // 构建MappedStatement 构建 MappedStatement 存放在 Configuration.ji sql查询的id
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }
  


解析SelectKeyNodes processSelectKeyNodes
  private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
    // sql节点中 获取 selectKey 节点
    List<XNode> selectKeyNodes = context.evalNodes("selectKey");
    
    // 如果 数据库配置了 DatabaseId(数据库厂商 方言)使用方言解析 额否则直接解析
    if (configuration.getDatabaseId() != null) {
      parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
    }
    parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
    
    
    removeSelectKeyNodes(selectKeyNodes);
  }
  
  private void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
    // 遍历获取到的节点
    for (XNode nodeToHandle : list) {
      // 设置id (由SQL语句的ID + !selectKey 组成)
      String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
      
      // 获取数据库驱动
      String databaseId = nodeToHandle.getStringAttribute("databaseId");
      
      // 如果驱动一致
      if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
        parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
      }
    }
  }
  
  private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
    // 在节点中 获取 resultType 结果类型
    String resultType = nodeToHandle.getStringAttribute("resultType");
    // 解析结果类型
    Class<?> resultTypeClass = resolveClass(resultType);
    
    // sql节点中 获取并解析 statementType  语句类型
    StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    
    // sql节点中 获取 keyProperty 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称
    String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
    
    // 在节点中 获取 keyColumn 返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称
    String keyColumn = nodeToHandle.getStringAttribute("keyColumn");
    
    // 在节点中 获取 order 设置selectKey的执行属性 在执行语句之前,还是之后
    boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));

    // 设置默认值 不适用缓存、没有多个结果集、没有Key生成器、返回大小为空、不设置超时时间、不刷新缓存、入参映射为空、出参映射为空、返回结果为空
    boolean useCache = false;
    boolean resultOrdered = false;
    KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
    Integer fetchSize = null;
    Integer timeout = null;
    boolean flushCache = false;
    String parameterMap = null;
    String resultMap = null;
    ResultSetType resultSetTypeEnum = null;

    // langDriver(语言驱动)创建SQL原数据 (设置不同节点的节点处理器)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
    
    // SQL执行类型为SELECT(查询)
    SqlCommandType sqlCommandType = SqlCommandType.SELECT;

    // 构建MappedStatement 构建 MappedStatement 存放在 Configuration.mappedStatements KEY为SelectKey的id
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null);

    id = builderAssistant.applyCurrentNamespace(id, false);

    // 并将当前SQL存在 configuration.keyGenerators 中 key为SQL语句的ID
    MappedStatement keyStatement = configuration.getMappedStatement(id, false);
    configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
  }

文章链接