mybatis源码学习-3-解析器模块

news2025/1/21 16:36:55

写在前面,这里会有很多借鉴的内容,有以下三个原因

  1. 本博客只是作为本人学习记录并用以分享,并不是专业的技术型博客
  2. 笔者是位刚刚开始尝试阅读源码的人,对源码的阅读流程乃至整体架构并不熟悉,观看他人博客可以帮助我快速入门
  3. 如果只是笔者自己观看,难免会有很多弄不懂乃至理解错误的地方,观看他人的体会能有效改善这个问题

1. 目录结构

在这里插入图片描述

  1. XNode类

    • 作用:XNode 类表示XML文档中的一个节点(Element或Node),它用于封装XML节点的信息,例如标签名、属性和子节点等。
    • 使用场景:MyBatis使用 XNode 来解析XML配置文件中的各种元素,如<select>, <update>, <insert>, <delete>等,并提供了一种方便的方式来访问这些元素的属性和子元素。
  2. PropertyParser类

    • 作用:PropertyParser 类是MyBatis用于解析属性表达式的工具类。属性表达式是在XML配置文件中引用JavaBean属性的方式,通常用于动态SQL。
    • 使用场景:MyBatis在解析动态SQL语句或属性表达式时,会使用 PropertyParser 来解析${}#{}中的属性表达式,并将其替换为实际的属性值或SQL参数。
  3. GenericTokenParser类

    • 作用:GenericTokenParser 类是MyBatis用于解析通用占位符(例如${}#{})的工具类。它允许你指定一个TokenHandler接口的实现,用于处理占位符内的内容。
    • 使用场景:MyBatis在解析SQL语句中的占位符时,会使用 GenericTokenParser 来查找和替换占位符内的内容,然后将处理后的SQL语句返回。
  4. ParsingException类

    • 作用:ParsingException 类是MyBatis中的自定义异常类,用于表示解析过程中的异常情况。它通常用于捕获和处理解析XML配置文件或SQL语句时可能发生的错误。
    • 使用场景:当MyBatis在解析配置文件或SQL语句时遇到不合法的语法或结构时,会抛出 ParsingException 异常,以便开发者识别和处理问题。
  5. TokenHandler接口

    • 作用:TokenHandler 接口是一个回调接口,定义了如何处理占位符内的内容。它用于与 GenericTokenParser 类一起工作,允许开发者自定义处理占位符内部内容的逻辑。
    • 使用场景:当MyBatis使用 GenericTokenParser 解析占位符时,它会调用 TokenHandler 接口的实现来处理占位符内的内容。开发者可以实现自定义的 TokenHandler 来定义处理逻辑,例如从配置文件或参数中获取属性值。
  6. XPathParser类

    • 作用:XPathParser 类是MyBatis中的XML解析工具,用于解析XML配置文件和映射文件。它提供了一种方便的方式来处理XML文档,包括节点遍历、属性获取、XPath表达式解析等。
    • 使用场景:XPathParser 类通常用于加载和解析MyBatis的XML配置文件,例如mybatis-config.xml和映射文件。它允许MyBatis以一种结构化的方式访问和操作XML配置文件中的内容。

2. XPathParser类

  1. 参数

XPathParser 类通常用于加载和解析MyBatis的XML配置文件,例如mybatis-config.xml和映射文件。它允许MyBatis以一种结构化的方式访问和操作XML配置文件中的内容。

public class XPathParser {
    private final Document document;
    private boolean validation;
    private EntityResolver entityResolver;
    private Properties variables;
    private XPath xpath;
    //省略
}
  • document属性,XML 被解析后,生成的org.w3c.dom.Document对象。

  • validation 属性,是否校验 XML 。一般情况下,值为 true

  • entityResolver属性,org.xml.sax.EntityResolver对象,XML 实体解析器。默认情况下,对 XML 进行校验时,会基于 XML 文档开始位置指定的 DTD 文件或 XSD 文件。例如说,解析mybatis-config.xml 配置文件时,会加载http://mybatis.org/dtd/mybatis-3-config.dtd这个 DTD 文件。但是,如果每个应用启动都从网络加载该 DTD 文件,势必在弱网络下体验非常下,甚至说应用部署在无网络的环境下,还会导致下载不下来,那么就会出现 XML 校验失败的情况。所以,在实际场景下,MyBatis 自定义了 EntityResolver 的实现,达到使用本地DTD 文件,从而避免下载网络DTD 文件的效果.

  • xpath 属性,javax.xml.xpath.XPath 对象,用于查询 XML 中的节点和元素。如果对 XPath 的使用不了解的胖友,请先跳转 《Java XPath 解析器 - 解析 XML 文档》 中,进行简单学习,灰常简单。

  • variables 属性,变量 Properties 对象,用来替换需要动态配置的属性值。例如:

    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
    
    • variables 的来源,即可以在常用的 Java 配置文件中配置,也可以使用 MyBatis <property /> 标签进行配置。例如:

      <properties resource="org/mybatis/example/config.properties">
        <property name="username" value="dev_user"/>
        <property name="password" value="F2Fa3!33TYyg"/>
      </properties>
      
      • 这里配置的 usernamepassword 属性,就可以替换上面的 ${username}${password} 这两个动态属性。
      • 具体如何实现的,可以看下面的 PropertyParser#parse(String string, Properties variables) 方法。
  1. 构造方法

解释完属性后,我们可以看见后面有一长串的构造方法,挑选其中一个

/**
 * 构造 XPathParser 对象
 *
 * @param xml XML 文件地址
 * @param validation 是否校验 XML
 * @param variables 变量 Properties 对象
 * @param entityResolver XML 实体解析器
 */
public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
    //1. 公共构造方法
    commonConstructor(validation, variables, entityResolver);
    //2. 创建document对象,将xml文件解析成一个document对象
    this.document = createDocument(new InputSource(new StringReader(xml)));
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    // 创建 XPathFactory 对象
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
}
/**
 * 创建 Document 对象,这里就是简单的调用别人提供的java xml api,就像我们调用netty提供的api那样
 *
 * @param inputSource XML 的 InputSource 对象
 * @return Document 对象
 */
private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
        // 1. 创建 DocumentBuilderFactory 对象
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        // 设置是否验证 XML
        factory.setValidating(validation); 

        factory.setNamespaceAware(false);
        factory.setIgnoringComments(true);
        factory.setIgnoringElementContentWhitespace(false);
        factory.setCoalescing(false);
        factory.setExpandEntityReferences(true);

        // 2. 创建 DocumentBuilder 对象
        DocumentBuilder builder = factory.newDocumentBuilder();
        // 设置实体解析器
        builder.setEntityResolver(entityResolver); 
        // 实现都空的
        builder.setErrorHandler(new ErrorHandler() { 

            @Override
            public void error(SAXParseException exception) throws SAXException {
                throw exception;
            }

            @Override
            public void fatalError(SAXParseException exception) throws SAXException {
                throw exception;
            }

            @Override
            public void warning(SAXParseException exception) throws SAXException {
            }

        });
        // 3. 解析 XML 文件
        return builder.parse(inputSource);
    } catch (Exception e) {
        throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
}

至此,我们可以通过构造方法创建一个对象对xml文件进行解析

例如,在org.apache.ibatis.parsing.XPathParserTest#shouldTestXPathParserMethods方法中,我们可以通过测试代码解析nodelet_test.xml中的元素,通过构造方法后获得的XPathParser对象已经对xml文件进行解析,如下

  1. eval元素

eval 元素的方法,用于获得 Boolean、Short、Integer、Long、Float、Double、String 类型的元素的值。我们以 #evalString(Object root, String expression) 方法为例子,代码如下:

public String evalString(Object root, String expression) {
    // 1. 获得指定元素或节点的值
    String result = (String) evaluate(expression, root, XPathConstants.STRING);
    // 2. 基于 variables 替换动态值,如果 result 为动态值,还记得variables是什么吗?Properties 对象,用来替换需要动态配置的属性值。
    result = PropertyParser.parse(result, variables);
    return result;
}
/**
 * 获得指定元素或节点的值,例如在上例中箭头所指表达式为"/employee/@id" , 节点为"[#document:null]" , 返回类型为String,那么返回的结果就是result = "${id_var}"
 *
 * @param expression 表达式
 * @param root       指定节点
 * @param returnType 返回类型
 * @return 值
 */
private Object evaluate(String expression, Object root, QName returnType) {
    try {
        return xpath.evaluate(expression, root, returnType);
    } catch (Exception e) {
        throw new BuilderException("Error evaluating XPath.  Cause: " + e, e);
    }
}

在这里没有使用动态替换,因此即便result = “${id_var}”,在经过PropertyParser#parse(String string, Properties variables)进行动态解析后,结果依然保持不变.

除了上面的vealString之类的方法,还有一个evalNode()方法,用户获得Node类型的节点的值,代码如下

//返回 Node 数组
public List<XNode> evalNodes(String expression) { 
    return evalNodes(document, expression);
}

public List<XNode> evalNodes(Object root, String expression) {
    // 1. 封装成 XNode 数组
    List<XNode> xnodes = new ArrayList<>();
    // 2. 获得 Node 数组
    NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
    for (int i = 0; i < nodes.getLength(); i++) {
        xnodes.add(new XNode(this, nodes.item(i), variables));
    }
    return xnodes;
}

//返回 Node 对象
public XNode evalNode(String expression) { 
    return evalNode(document, expression);
}

public XNode evalNode(Object root, String expression) {
    //1. 获得 Node 对象
    Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
    if (node == null) {
        return null;
    }
    //2. 封装成 XNode 对象
    return new XNode(this, node, variables);
}

1处,返回结果有 Node 对象数组两种情况,根据方法参数 expression 需要获取的节点不同。

2处,会将结果Node封装为org.apache.ibatis.parsing.XNode对象,主要为了动态值的替换,例如测试方法org.apache.ibatis.parsing.XPathParserTest#shouldTestXPathParserMethods调用parser.evalNode("/employee/@id").toString().trim()这条语句时,会产生下面的一个XNode对象,也就是获取xml文件中id节点的值了

在这里插入图片描述

这个类基本看完了,今天就到这里了,记录一下

------------------------2023/9/3 未完待续------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/966452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++实现蜂群涌现效果(flocking)

Flocking算法0704_元宇宙中的程序员的博客-CSDN博客 每个个体的位置&#xff0c;通过计算与周围个体的速度、角度、位置&#xff0c;去更新位置。

【01背包理论】01背包问题dp[i][j](二维数组) <动态规划板子>

【01背包理论】01背包问题 dp[i][j] 有 n 件物品和一个最多能背重量为 w 的背包。 第 i 件物品的重量是 weight[i]&#xff0c;得到的价值是 value[i] 。 每件物品只有一个&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 题解 动态规划 确定 dp 数组以及下标的含义…

使用Docker安装和部署RabbitMQ

&#x1f680; 1 拉取RabbitMQ Docker镜像 首先&#xff0c;使用Docker命令从Docker Hub拉取RabbitMQ官方镜像。打开终端并运行以下命令&#xff1a; docker pull rabbitmq&#x1f680; 2 创建RabbitMQ容器 一旦镜像下载完成&#xff0c;使用以下命令创建RabbitMQ容器&…

报错合集 ing - net::ERR_ABORTED 500 (Internal Server Error)

报错&#xff1a;net::ERR_ABORTED 500 (Internal Server Error) 根据提示找到对应文件 解决&#xff1a;检查代码&#xff0c;根据高亮颜色判断&#xff0c;发现箭头函数漏了一个>。 报错&#xff1a;Uncaught TypeError: Assignment to constant variable. &#xff08…

【负载均衡】常见的负载均衡策略有哪些?

文章目录 前言负载均衡分类常见负载均衡策略小结 前言 负载均衡策略是实现负载均衡器的关键&#xff0c;而负载均衡器又是分布式系统中不可或缺的重要组件。使用它有助于提高系统的整体性能、可用性、可靠性和安全性&#xff0c;同时支持系统的扩展和故障容忍性。对于处理大量…

新建工程——第一个S32DS工程

之前的"测试开发板"章节 测试开发板——第一个AutoSAR程序,使用了一个 demo 工程,不管是裸机程序还是 AutoSAR 程序,那都是别人已经创建好的工程。本节来介绍如何来创建自己的工程,本节介绍如何创建一个 S32DS 的工程,点亮开发板上的 LED 我们从官方提供的例程…

C++(20):多重继承与虚继承

多重继承 是指从多个直接基类中产生派生类的能力。多重继承的派生类继承了所有父类的属性。 多重继承 在派生类的派生列表中可以包含多个基类&#xff1a; class Bear : public zooAnimal { class Panda : public Bear, public Endangered{/* ...*/};每个基类包含一个可选的…

Android 大图显示优化方案-加载Gif 自定义解码器

基于Glide做了图片显示的优化&#xff0c;尤其是加载Gif图的优化&#xff0c;原生Glide加载Gif图性能较低。在原生基础上做了自定义解码器的优化&#xff0c;提升Glide性能 Glide加载大图和Gif 尤其是列表存在gif时&#xff0c;会有明显卡顿&#xff0c;cpu和内存占用较高&…

Android学习之路(13) Handler详解

1. 简介 Handler是一套 Android 消息传递机制,主要用于线程间通信。 用最简单的话描述&#xff1a; handler其实就是主线程在起了一个子线程&#xff0c;子线程运行并生成Message&#xff0c;Looper获取message并传递给Handler&#xff0c;Handler逐个获取子线程中的Message.…

RT-Thread 中断管理学习(二)

中断的底半处理 RTT不对中断服务程序所需要的处理时间做任何假设、限制&#xff0c;但如图其它实时操作系统或非实时操作系统一样&#xff0c;用户需要保证所有的中断服务程序在尽可能短的时间内完成&#xff08;中断服务程序在系统中相当于拥有最高的优先级&#xff0c;会抢占…

论文研读-SIMD系列-利用BMI指令进行选择下推

利用位操作指令BMI在列存中进行选择下推 Selection Pushdown in Column Stores using Bit Manipulation Instructions 列存能够提供高效的压缩能力&#xff0c;所以当前分析型数据库系统都基于列存储。然而&#xff0c;查询处理时&#xff0c;压缩会面临解码速率的挑战。以往研…

亚马逊产品流量上不去怎么办?亚马逊产品流量入口有哪些?

众所周知流量对于跨境卖家们是很重要的&#xff0c;这影响了你产品的曝光度和转化率&#xff0c;那么如果亚马逊产品流量上不去怎么办&#xff0c;亚马逊产品流量入口有哪些&#xff1f; 亚马逊产品流量上不去怎么办&#xff1f; 1、优化产品标题和关键词 产品标题和关键词是…

mojo初体验

目录标题 mojo初体验试用地址变量定义参数可变性和所有权Structures后续 mojo初体验 试用地址 https://www.modular.com/get-started 与python基础语法很相似。 变量定义 let定义不可变变量var定义可变变量 参数可变性和所有权 下面是一个基本的函数&#xff1a; fn add…

第一章 计算机概述

1.冯诺依曼结构&#xff1a; 计算机由运算器、控制器、存储器、输入设备、输出设备五大部件组成 运算器和控制器称为CPU&#xff1b;CPU和存储器称为计算机主机&#xff1b;其余输入、输出设备、外存储器称为计算机外部设备采用二进制表示数据和指令 指令由操作码&#xff08;…

AJAX学习笔记1发送Get请求

传统请求有哪些方式,及缺点 传统请求有哪些? 1.直接在浏览器地址栏上输入URL. 2.点击超连接. <a href"/上下文/请求地址">超链接请求</a> ---->相对路径 <a href"http://www.baidu.com">超链接请求</a> ---->绝对路…

【Java 基础篇】Java StringBuffer详解:更高效的字符串处理

在Java编程中&#xff0c;字符串是一个常见的数据类型&#xff0c;用于存储文本信息。然而&#xff0c;与字符串相关的操作可能会导致性能问题&#xff0c;因为字符串是不可变的&#xff0c;每次对字符串进行操作都会创建一个新的字符串对象。为了解决这个问题&#xff0c;Java…

Windows命令行初步:更改配色、提示符以及编码方式

文章目录 启动和退出窗口标题和提示符命令行颜色更改编码 启动和退出 按下winR&#xff0c;调出运行窗口&#xff0c;输入cmd就可以进入命令行了。在Win10以前的系统种&#xff0c;如果在命令行中再输入一个cmd&#xff0c;就会再打开一个命令行。但最近的Win11版本中&#xf…

[管理与领导-66]:IT基层管理者 - 辅助技能 - 4- 乌卡时代(VUCA )的团队管理思维方式的转变

目录 一、乌卡时代人与公司的关系的转变 二、乌卡时代管理方式的转变 三、乌卡时代的管理与传统时代的管理比较 四、乌卡时代管理者的挑战 五、乌卡时代如何做好管理 六、个人能力要求 一、乌卡时代人与公司的关系的转变 在乌卡时代&#xff08;指虚拟办公、远程工作等数…

实景三维数字孪生系统(实景三维电子沙盘)

一、概况 实景三维数字孪生系统是一种基于虚拟现实技术的系统&#xff0c;紧紧围绕应急处置的核心业务&#xff0c;通过将真实世界的物体、场景和过程数字化&#xff0c;创建出一个与真实世界相对应的虚拟模型。它可以模拟真实世界中的各种情境和操作&#xff0c;使用户能够在虚…

PYTHON知识点学习-函数调用中returnprint

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是Aileen★。希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由 Aileen_0v0★ 原创 CSDN首发&#x1f412; 如需转载还请通知⚠ &am…