第三章-Mybatis源码解析-以xml方式走流程-mapper解析(一)

讲解前,我们先思考几个问题:

1、谁来解析?

2、用什么解析?

3、解析成什么?

4、解析结果如何存放?

5、最终用途?

那么,我们顺着上面几个问题来解读源码。

3.1 XMLMapperBuilder对象创建

第二章了解,解析前要先创建 XMLMapperBuilder 对象,代码如下:

XMLConfigBuilder.mapperElement方法

java 复制代码
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, 			       configuration.getSqlFragments());
mapperParser.parse();

XMLMapperBuilder对象创建

java 复制代码
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    // 进入XPathParser类,可了解到,最终是利用 XPath 来解析xml,XPath是Java自带的一个接口,在javax.xml.xpath包中
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }

  private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);  // 父类构造函数调用,看BaseBuilder类
    // 创建 MapperBuilderAssistant 构建mapper的辅助类,先不管细节,后面用到时再讲
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    // 下面都是赋值
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }

public abstract class BaseBuilder {
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration; // 赋值
    // 设置类型别名注册器,先不管细节,后面用到时再讲
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    // 设置类型处理器注册器,先不管细节,后面用到时再讲
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
}

以上代码就可以解答问题1和问题2:由XMLMapperBuilder来解析,内部通过XPath来解析xml,XPath怎么解析xml,这里不会细讲,有兴趣的读者可以自行查阅资料学习。

3.2 XML配置文件解析

3.2.1 解析过程

进入 XMLMapperBuilder.parse() 方法

java 复制代码
public void parse() {
    // 判断是不是已经加载过资源,避免重复加载
    if (!configuration.isResourceLoaded(resource)) {
      // 从根节点mapper开始解析
      configurationElement(parser.evalNode("/mapper"));
      // 解析完了,添加到已加载资源中,里面用的是一个HashSet集合来存放,主要是为了防止重复加载
      configuration.addLoadedResource(resource);
      // 给命名空间构建mapper(前提是命名空间必须是可加载的类,这个主要用在注解方式中)
      bindMapperForNamespace();
    }
    // 清空未处理完的result map
    parsePendingResultMaps();
    // 清空未处理完的Cache ref
    parsePendingCacheRefs();
    // 清空未处理完的statement
    parsePendingStatements();
  }

3.2.2 mapper解析细节

入口还是在 XMLMapperBuilder 类中

java 复制代码
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");
      }
      // 给 MapperBuilderAssistant 辅助对象,设置命名空间,后面会用到
      builderAssistant.setCurrentNamespace(namespace);
      // cache-ref节点解析,看章节`3.2.2.1`
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      // 用于引用外部 parameterMap 的属性,目前已被废弃,既然废弃了,就不讲了
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      // resultMap 节点解析,看章节`3.2.2.3`
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      // sql 节点解析,看章节`3.2.2.4`
      sqlElement(context.evalNodes("/mapper/sql"));
      // 重点来了,真正的sql语句 节点解析,看章节`3.2.2.5`
      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);
    }
  }

在讲解mapper下各子节点的解析前,先了解下它有哪些子节点,以及各自的用途,SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

  • cache -- 该命名空间的缓存配置。
  • cache-ref -- 引用其它命名空间的缓存配置。
  • resultMap -- 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterMap -- 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
  • sql -- 可被其它语句引用的可重用语句块。
  • insert -- 映射插入语句。
  • update -- 映射更新语句。
  • delete -- 映射删除语句。
  • select -- 映射查询语句。

后续将通过抖音视频/直播的形式分享技术,由于前期要做一些准备和规划,预计2024年6月开始,欢迎关注,如有需要或问题咨询,也可直接抖音沟通交流。

相关推荐
qq_589568101 小时前
springbootweb案例,出现访问 http://localhost:8080/list 一直处于浏览器运转阶段
java·网络协议·http·list·springboot
JAVA面经实录9172 小时前
计算机基础(完整版·超详细可背诵)
java·linux·数据结构·算法
AC赳赳老秦2 小时前
知识产权辅助:用 OpenClaw 批量生成专利交底书 / 软著申请材料,自动校验格式与内容合规性
java·人工智能·python·算法·elasticsearch·deepseek·openclaw
FYKJ_20103 小时前
springboot校园兼职平台--附源码02041
java·javascript·spring boot·python·eclipse·django·php
书源丶4 小时前
三十六、File 类与 IO 流基础——文件操作的「第一步」
java
AI人工智能+电脑小能手4 小时前
【大白话说Java面试题】【Java基础篇】第30题:JDK动态代理和CGLIB动态代理有什么区别
java·开发语言·后端·面试·代理模式
DFT计算杂谈4 小时前
wannier90 参数详解大全
java·前端·css·html·css3
marsh02065 小时前
43 openclaw熔断与降级:保障系统在异常情况下的可用性
java·运维·网络·ai·编程·技术
张健11564096485 小时前
临界区和同一线程上锁
java·开发语言·jvm