Mybatis源码解读(XML解析篇)

1,878次阅读
没有评论

共计 6616 个字符,预计需要花费 17 分钟才能阅读完成。

Mybatis源码解读(XML解析篇)
Mybatis并不直接解析XML,底层的XML是XPath解析的,经由Mybatis的XPathParser解析后成为Mybatis的XNode,本篇也从这里展开。核心在于BaseBuilder这个基类,它的实现如下图
Mybatis源码解读(XML解析篇)

XMLConfigBuilder

XMLConfigBuilder是用来解析配置文件的,对于习惯了Springboot的开发者,这是陌生的。因为我们通常都依赖的自动装配,如下所示

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class) //启用了属性配置
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean ...

这里启用了MybatisProperties配置,进而通过Springboot配置文件的形式配置了Configuration

@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {

  public static final String MYBATIS_PREFIX = "mybatis";

  private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();

  /**
   * Location of MyBatis xml config file.
   */
  private String configLocation;

  /**
   * Locations of MyBatis mapper files.
   */
  private String[] mapperLocations;

  /**
   * Packages to search type aliases. (Package delimiters are ",; \t\n")
   */
  private String typeAliasesPackage;

  /**
   * The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that
   * searched from typeAliasesPackage.
   */
  private Class<?> typeAliasesSuperType;

  /**
   * Packages to search for type handlers. (Package delimiters are ",; \t\n")
   */
  private String typeHandlersPackage;

  /**
   * Indicates whether perform presence check of the MyBatis xml config file.
   */
  private boolean checkConfigLocation = false;

  /**
   * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
   */
  private ExecutorType executorType;

  /**
   * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
   */
  private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;

  /**
   * Externalized properties for MyBatis configuration.
   */
  private Properties configurationProperties;

  /**
   * A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is
   * not used.
   */
  @NestedConfigurationProperty
  private Configuration configuration; // Mybatis配置文件 ...

现在有一个配置文件MinimalMapperConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--

       Copyright 2009-2016 the original author or authors.

       Licensed under the Apache License, Version 2.0 (the "License");
       you may not use this file except in compliance with the License.
       You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       See the License for the specific language governing permissions and
       limitations under the License.

-->
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>


</configuration>

最精简的配置,啥也没配置,我们通过XMLConfigBuilder来解析它

String resource = "MinimalMapperConfig.xml";
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
  XMLConfigBuilder builder = new XMLConfigBuilder(inputStream);
  Configuration config = builder.parse();
}

返回的是同一个类型Configuration,解析如下,显然经由XPathParser解析得到XNode,然后解析各个属性值
Mybatis源码解读(XML解析篇)

这里mapperElement(root.evalNode(“mappers”)),其实读取了类似这样的配置

Mybatis源码解读(XML解析篇)

看看实现,原来调用XMLMapperBuilder处理了

Mybatis源码解读(XML解析篇)

XMLMapperBuilder

XMLMapperBuilder,从名字猜测就是解析我们DAO对应的Mapper文件的,其实它负责解析mapper节点下的parameterMap,resultMap,sql之类的节点,select|insert|update|delete这类节点它也只负责引导

Mybatis源码解读(XML解析篇)

最终是交给XMLStatementBuilder去处理的

Mybatis源码解读(XML解析篇)

XMLStatementBuilder

XMLStatementBuilder的解析主要实现如下

public void parseStatementNode() {
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }

  String nodeName = context.getNode().getNodeName();
  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);

  // Include Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

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

  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  // Parse selectKey after includes and remove them.
  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;
  }
  //最终的Sql来源
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  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");
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultSetType = context.getStringAttribute("resultSetType");
  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");

  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

到这里,可以发现Sq的最终生成是经由LanguageDriver处理的,默认的XML实现是XMLLanguageDriver,又到了XMLScriptBuilder的主战场
Mybatis源码解读(XML解析篇)

XMLScriptBuilder

我们熟悉的where,if等标签出现了!
Mybatis源码解读(XML解析篇)
nodeHandlerMap注册了各种NodeHandler的执行策略,不同的标签分发给不同的实现去处理

MapperBuilderAssistant

这个就比较特殊了,主要起辅助作用,XMLMapperBuilder以及XMLStatementBuilder等等都有它的身影,通过组合模式来使用它的功能。

SqlSourceBuilder

SqlSourceBuilder主要是来生成SqlSource的,解析#{}之类的参数引用。

Configuration

经过这一系列操作,Configuration这个主角的很多属性得到了填充,这也是和Spring整合的关键所在,整合相关解读,下篇将探讨
Mybatis源码解读(XML解析篇)

正文完
 
mysteriousman
版权声明:本站原创文章,由 mysteriousman 2022-05-15发表,共计6616字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)