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


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

    1、谁来解析?

    2、用什么解析?

    3、解析成什么?

    4、解析结果如何存放?

    5、最终用途?

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

    3.1 XMLMapperBuilder对象创建

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

    XMLConfigBuilder.mapperElement方法

    XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, 			       configuration.getSqlFragments());
    mapperParser.parse();
    
    • 1
    • 2

    XMLMapperBuilder对象创建

    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

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

    3.2 XML配置文件解析

    3.2.1 解析过程

    进入 XMLMapperBuilder.parse() 方法

    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();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.2.2 mapper解析细节

    入口还是在 XMLMapperBuilder 类中

    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);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

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

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

  • 相关阅读:
    中国人民大学与加拿大女王大学金融硕士——不忘初心,点燃梦想之火
    curl 访问 IPv6 url
    不安装运行时运行.NET程序
    微信朋友圈全新玩法,轻松互动,引爆你的社交圈
    uniapp 使用图表
    【C语言】【动态内存管理】malloc,free,calloc,realloc
    【区块链 | 预言机】价格预言机的使用总结(二):UniswapV2篇
    剑指Offer || :栈与队列(简单)
    【Scala专栏】方法和函数
    DispatcherServlet的功能简介说明
  • 原文地址:https://blog.csdn.net/zhang527294844/article/details/136329574