抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

MyBatis初始化

会加载mybatis-config.xml配置文件、Mapper.xml映射配置文件以及Mapper接口中的注解信息,解析后的配置信息会形成相应的对象并保存到Configuration对象中。

  1. 解析mybatis-config.xml配置文件
    • SqlSessionFactoryBuilder
    • XMLConfigBuilder
    • Configuration
  2. 解析Mapper.xml映射文件
    • XMLMapperBuilder::parse()
    • XMLStatementBuilder::parseStatementNode()
    • XMLLanguageDriver
    • SqlSource
    • MappedStatement
  3. 解析Mapper接口中的注解
    • MapperRegistry
    • MapperAnnotationBuilder::parse()

解析mybatis-config.xml配置文件

MyBatis 的初始化流程的入口是SqlSessionFactoryBuilder::build(Reader reader, String environment, Properties properties)方法,具体流程如下:

SqlSessionFactoryBuilder::builder -> XMLConfigBuilder::parse() -> XPathParser::parse() -> 返回XNode -> 返回Configuration -> 创建DefaultSqlSessionFactory

org.apache.ibatis.session.SqlSessionFactoryBuilder

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
 try {
   XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
   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.
   }
 }
}

首先会使用XMLConfigBuilder::parser()解析mybatis-config.xml配置文件

  • 先解析标签configuration内的数据封装成XNodeconfiguration也是MyBatis中最重要的一个标签。
  • 根据XNode解析mybatis-config.xml配置文件的各个标签转变为各个对象。

org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration

private void parseConfiguration(XNode root) {
 try {
   // issue #117 read properties first
   propertiesElement(root.evalNode("properties"));
   Properties settings = settingsAsProperties(root.evalNode("settings"));
   loadCustomVfs(settings);
   loadCustomLogImpl(settings);
   typeAliasesElement(root.evalNode("typeAliases"));
   pluginElement(root.evalNode("plugins"));
   objectFactoryElement(root.evalNode("objectFactory"));
   objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
   reflectorFactoryElement(root.evalNode("reflectorFactory"));
   settingsElement(settings);
   // read it after objectFactory and objectWrapperFactory issue #631
   environmentsElement(root.evalNode("environments"));
   databaseIdProviderElement(root.evalNode("databaseIdProvider"));
   typeHandlerElement(root.evalNode("typeHandlers"));
   mapperElement(root.evalNode("mappers"));
 } catch (Exception e) {
   throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
 }
}

再基于Configuration使用SqlSessionFactoryBuilder::build()生成DefaultSqlSessionFactory供给后续执行使用。

解析Mapper.xml映射配置文件

首先使用XMLMapperBuilder::parse()解析Mapper.xml,具体流程

XMLMapperBuilder::parse() -> XPathParser::parse() -> 返回XNode -> XMLMapperBuilder#configurationElement() -> buildStatementFromContext() -> XMLStatementBuilder -> XMLLanguageDriver::createSqlSource -> 返回SqlSource -> XMLStatementBuilder::parseStatementNode -> builderAssistant.addMappedStatement -> 返回MappedStatement -> XMLMapperBuilder::bindMapperForNamespace

通过XPathParser::evalNodemapper标签中内容解析到XNode

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse

public void parse() {
 if (!configuration.isResourceLoaded(resource)) {
   configurationElement(parser.evalNode("/mapper"));
   configuration.addLoadedResource(resource);
   bindMapperForNamespace();
 }

 parsePendingResultMaps();
 parsePendingCacheRefs();
 parsePendingStatements();
}

再由configurationElement()方法去解析XNode中的各个标签

  • namespace
  • parameterMap
  • resultMap
  • select|insert|update|delete

org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement

private void configurationElement(XNode context) {
 try {
   String namespace = context.getStringAttribute("namespace");
   if (namespace == null || namespace.isEmpty()) {
     throw new BuilderException("Mapper's namespace cannot be empty");
   }
   builderAssistant.setCurrentNamespace(namespace);
   cacheRefElement(context.evalNode("cache-ref"));
   cacheElement(context.evalNode("cache"));
   parameterMapElement(context.evalNodes("/mapper/parameterMap"));
   resultMapElements(context.evalNodes("/mapper/resultMap"));
   sqlElement(context.evalNodes("/mapper/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);
 }
}

其中,基于XMLMapperBuilder::buildStatementFromContext(),遍历XNode节点们,逐个创建XMLStatementBuilder对象,然后通过XMLStatementBuilder::parseStatementNode()进行解析

  • parameterType
  • resultType
  • selectKey等

并会通过LanguageDriver::createSqlSource()(默认XmlLanguageDriver)解析动态sql生成SqlSource

使用GenericTokenParser::parser()负责将 SQL 语句中的 #{} 替换成相应的 ? 占位符,并获取该 ? 占位符对应的的org.apache.ibatis.mapping.ParameterMapping对象。

还通过MapperBuilderAssistant::addMappedStatement()生成MappedStatement

public void parseStatementNode() {
    // 获取id属性,编号
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    // 判断 databaseId 是否匹配
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    // 解析获得各种属性
    String nodeName = context.getNode().getNodeName();
    // 获得 SQL 对应的 SqlCommandType 枚举值
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // 创建 XMLIncludeTransformer 对象,并替换 <include /> 标签相关的内容
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    String parameterType = context.getStringAttribute("parameterType");
    
    Class<?> parameterTypeClass = resolveClass(parameterType);

    String lang = context.getStringAttribute("lang");
    // 获得 lang 对应的 LanguageDriver 对象
    LanguageDriver langDriver = getLanguageDriver(lang);
   
    // 解析 <selectKey /> 标签
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
    // 创建 SqlSource 生成动态sql
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    // 获得 statementType 对应的枚举值
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    
    String resultType = context.getStringAttribute("resultType");
    // 获得 resultType 对应的类
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    // 获得 resultSet 对应的枚举值
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");
    // 创建 MappedStatement 对象
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

解析Mapper接口中的注解

当执行完XMLMapperBuilder::configurationElement()方法后,会调用XMLMapperBuilder::bindMapperForNamespace()会转换成对接口上注解进行扫描,具体通过MapperRegistry::addMapper()调用MapperAnnotationBuilder实现的。

MapperAnnotationBuilder::parse()是注解构造器,负责解析Mapper接口上的注解,解析时需要注意避免和XMLMapperBuilder::parse()方法冲突,重复解析,最终使用parseStatement解析。

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse

public void parse() {
 String resource = type.toString();
 //判断当前 Mapper 接口是否应加载过
 if (!configuration.isResourceLoaded(resource)) {
   // 加载对应的 XML Mapper,注意避免和 XMLMapperBuilder::parse() 方法冲突
   loadXmlResource();
   // 标记该 Mapper 接口已经加载过
   configuration.addLoadedResource(resource);
   assistant.setCurrentNamespace(type.getName());
   // 解析 @CacheNamespace 注解
   parseCache();
   parseCacheRef();
   // 遍历每个方法,解析其上的注解
   for (Method method : type.getMethods()) {
     if (!canHaveStatement(method)) {
       continue;
     }
     if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
         && method.getAnnotation(ResultMap.class) == null) {
       parseResultMap(method);
     }
     try {
       // 执行解析
       parseStatement(method);
     } catch (IncompleteElementException e) {
       configuration.addIncompleteMethod(new MethodResolver(this, method));
     }
   }
 }
 // 解析待定的方法
 parsePendingMethods();
}

parseStatement()Mapper.xml解析流程类似

  • 通过加载LanguageDriver, GenericTokenParser等为生成SqlSource动态sql作准备
  • 使用MapperBuilderAssistant::addMappedStatement()生成注解@mapper, @CacheNamespace等的MappedStatement信息
void parseStatement(Method method) {
    // 获取接口参数类型
    final Class<?> parameterTypeClass = getParameterType(method);
    // 加载语言处理器,默认XmlLanguageDriver
    final LanguageDriver languageDriver = getLanguageDriver(method);

    getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {
      // 生成动态SQL
      final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);
      final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
      final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options)x.getAnnotation()).orElse(null);
      final String mappedStatementId = type.getName() + "." + method.getName();

      final KeyGenerator keyGenerator;
      String keyProperty = null;
      String keyColumn = null;
      if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
        // 如果有 @SelectKey 注解,则进行处理
        SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey)x.getAnnotation()).orElse(null);
        if (selectKey != null) {
          keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
          keyProperty = selectKey.keyProperty();
        } else if (options == null) {
          // 如果无 @Options 注解,则根据全局配置处理
          keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        } else {
          // 如果有 @Options 注解,则使用该注解的配置处理
          keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
          keyProperty = options.keyProperty();
          keyColumn = options.keyColumn();
        }
      } else {
        // 无
        keyGenerator = NoKeyGenerator.INSTANCE;
      }

      Integer fetchSize = null;
      Integer timeout = null;
      StatementType statementType = StatementType.PREPARED;
      ResultSetType resultSetType = configuration.getDefaultResultSetType();
      boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
      boolean flushCache = !isSelect;
      boolean useCache = isSelect;
      // 初始化各种属性
      if (options != null) {
        if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
          flushCache = true;
        } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
          flushCache = false;
        }
        useCache = options.useCache();
        fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
        timeout = options.timeout() > -1 ? options.timeout() : null;
        statementType = options.statementType();
        if (options.resultSetType() != ResultSetType.DEFAULT) {
          resultSetType = options.resultSetType();
        }
      }
      // 获得 resultMapId 编号字符串
      String resultMapId = null;
      if (isSelect) {
        //如果有 @ResultMap 注解,使用该注解为 resultMapId 属性
        ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
        if (resultMapAnnotation != null) {
          resultMapId = String.join(",", resultMapAnnotation.value());
        } else {
          // 如果无 @ResultMap 注解,解析其它注解,作为 resultMapId 属性
          resultMapId = generateResultMapName(method);
        }
      }
    // 构建 MappedStatement 对象
    assistant.addMappedStatement(
          mappedStatementId,
          sqlSource,
          statementType,
          sqlCommandType,
          fetchSize,
          timeout,
          // ParameterMapID
          null,
          parameterTypeClass,
          resultMapId,
          getReturnType(method),
          resultSetType,
          flushCache,
          useCache,
          // TODO gcode issue #577
          false,
          keyGenerator,
          keyProperty,
          keyColumn,
          statementAnnotation.getDatabaseId(),
          languageDriver,
          // ResultSets
          options != null ? nullOrEmpty(options.resultSets()) : null);
    });
}

生成动态SqlSource

首先需要获取langDriver实现XMLLanguageDriver / RawLanguageDriver,现在使用默认的XMLLanguageDriver::createSqlSource(configuration, context, parameterTypeClass)来创建,再使用XMLScriptBuilder::parseScriptNode()解析生成SqlSource

  • DynamicSqlSource:动态的 SqlSource 实现类 , 适用于使用了 OGNL 表达式,或者使用了${}表达式的SQL
  • RawSqlSource:原始的 SqlSource 实现类 , 适用于仅使用 #{} 表达式,或者不使用任何表达式的情况

org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode

public SqlSource parseScriptNode() {
 MixedSqlNode rootSqlNode = parseDynamicTags(context);
 SqlSource sqlSource;
 if (isDynamic) {
   sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
 } else {
   sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
 }
 return sqlSource;
}

分析一下RawSqlSource

public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
 SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
 Class<?> clazz = parameterType == null ? Object.class : parameterType;
 sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}

使用SqlSourceParser::parse()去解析SQL。

org.apache.ibatis.builder.SqlSourceBuilder#parse

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
 ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
 GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
 String sql;
 if (configuration.isShrinkWhitespacesInSql()) {
   sql = parser.parse(removeExtraWhitespaces(originalSql));
 } else {
   sql = parser.parse(originalSql);
 }
 return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

ParameterMappingTokenHandlerSqlSourceBuilder的内部私有静态类, ParameterMappingTokenHandler负责将匹配到的 #{} 对,替换成相应的 ? 占位符,并获取该 ? 占位符对应的 org.apache.ibatis.mapping.ParameterMapping对象。并基于ParameterMappingTokenHandler使用GenericTokenParser::parse()将SQL中的#{}转化占位符?,占位符后创建一个StaticSqlSource 返回。

总结

在MyBatis初始化过程中,会加载mybatis-config.xml配置文件、Mapper.xml映射配置文件以及Mapper接口中的注解信息,解析后的配置信息会形成相应的对象并全部保存到Configuration对象中,并创建DefaultSqlSessionFactory供SQl执行过程创建出顶层接口SqlSession供给用户进行操作。

评论