栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

源码阅读-MybatisMapperXml文件解析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

源码阅读-MybatisMapperXml文件解析

1、XMLConfigBuilder

mapper.xml文件解析由XMLConfigBuilder类开始,代码如下:

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        //从mapperconfig文件中读取mapperxml文件信息
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          //开始解析mapperxml文件
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          //开始解析 见2.1、XMLMapperBuilder#pare
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          Class mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

parent:mapper.config.xml文件件被解析后封装到XNode中。

2、XMLMapperBuilder 2.1、XMLMapperBuilder#pare
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    //解析mapper标签,SQL的解析入口在这儿见2.1、XMLMapperBuilder#configurationElement
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    //绑定命名空间
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}
2.1、XMLMapperBuilder#configurationElement
 private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        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"));
     //获取select|insert|update|delete等标签信息并进行解析
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

获取select|insert|update|delete等标签信息为一个list集合,最终会被封装到XMLStatementBuilder中进行解析并且每个配置文件都会被最终解析为一个MappedStatement。详见3、MappedStatement

 private void buildStatementFromContext(List list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }
3、MappedStatement 3.1、MappedStatement#parseStatementNode
public void parseStatementNode() {
    
  	
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
    
    // Parse the SQL (pre:  and  were parsed and removed)
  	//此处会正真开始进行SQL的解析。其中context为XNode,即2.1中select等标签被解析后的XNode
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
		//....
  }

langDriver:我认为就是一个解析器。默认为XMLLanguageDriver,另一种为RawLanguageDriver。本章我们以XMLLanguageDriver为例子进行讲解。

langDriver.createSqlSource内部使用XMLscriptBuilder进行解析XNode。最终将解析后的sqlSource保存到了mapperstatement中,并且在程序运行中会被调用。

public class XMLLanguageDriver implements LanguageDriver {
  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class parameterType) {
    XMLscriptBuilder builder = new XMLscriptBuilder(configuration, script, parameterType);
    return builder.parsescriptNode();
  }
}

configuration:全局配置,每个配置文件一个

script:mappxml文件解析后会被封装到XNode中

4、XMLscriptBuilder 4.1、XMLscriptBuilder#parsescriptNode

首先通过parseDynamicTags会对XNode的context解析为最小单元的SqlNode。解析结果会最终确认是否为动态SQL(动态SQL指标签类型为element类型的标签)

 public SqlSource parsescriptNode() {
   //对XNode进行解析为最小单元的SqlNode,解析结果会最终确认是否为动态SQL(动态SQL指标签类型为element类型的标签),详见4.2XMLscriptBuilder#parseDynamicTags
    List contents = parseDynamicTags(context);
   //最终被封装到MixedSqlNode中
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    if (isDynamic) {
      //由于我们当前SQL是动态SQL所以最终会被封装到此类中。
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
4.2、XMLscriptBuilder#parseDynamicTags

XNode解析,解析结果会最终确认是否为动态SQL(动态SQL指标签类型为element类型的标签)

List parseDynamicTags(XNode node) {
    List contents = new ArrayList();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      //判断node类型是否为文本类型等,
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
       //Node类型为element类型比如
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        String nodeName = child.getNode().getNodeName();
        //此方法内部提供了mybatis支持的所有动态标签,见4.3、XMLscriptBuilder#nodeHandlers
        NodeHandler handler = nodeHandlers(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
        //设置为动态SQL
        isDynamic = true;
      }
    }
    return contents;
  }

以如下select为例进行讲解