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

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

  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 constructortry {// 1. 创建 DocumentBuilderFactory 对象DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();// 设置是否验证 XMLfactory.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() { @Overridepublic void error(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic void fatalError(SAXParseException exception) throws SAXException {throw exception;}@Overridepublic 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.rhkb.cn/news/117456.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WGCNA分析教程 | 代码四

写在前面 WGCNA的教程&#xff0c;我们在前期的推文中已经退出好久了。今天在结合前期的教程的进行优化一下。只是在现有的教程基础上&#xff0c;进行修改。其他的其他并无改变。 前期WGCNA教程 WGCNA分析 | 全流程分析代码 | 代码一 WGCNA分析 | 全流程分析代码 | 代码二 …

SpringCloudGateway集成SpringDoc

SpringCloudGateway集成SpringDoc 最近在搞Spring版本升级&#xff0c;按客户要求升级Spring版本&#xff0c;原来用着SpringBoot 2.2.X版本&#xff0c;只需要升级SpringBoot 2.X最新版本也就可以满足客户Spring版本安全要求&#xff0c;可是好像最新的SpringBoot 2.X貌似也不…

Ubuntu入门03——Ubuntu用户操作

1.Ubuntu如何进入root用户 进入ROOT用户的指令&#xff1a; Linux用su命令来切换用户&#xff1a; su root执行命令后&#xff0c;会提示你输入密码&#xff0c;而Ubuntu是没有设置root初始密码的。 若su命令不能切换root&#xff0c;提示su: Authentication failure&#x…

React笔记(三)类组件(1)

一、组件的概念 使用组件方式进行编程&#xff0c;可以提高开发效率&#xff0c;提高组件的复用性、提高代码的可维护性和可扩展性 React定义组件的方式有两种 类组件&#xff1a;React16.8版本之前几乎React使用都是类组件 函数组件:React16.8之后&#xff0c;函数式组件使…

C++面试题(期)-数据库(二)

目录 1.3 事务 1.3.1 说一说你对数据库事务的了解 1.3.2 事务有哪几种类型&#xff0c;它们之间有什么区别&#xff1f; 1.3.3 MySQL的ACID特性分别是怎么实现的&#xff1f; 1.3.4 谈谈MySQL的事务隔离级别 1.3.5 MySQL的事务隔离级别是怎么实现的&#xff1f; 1.3.6 事…

机器学习基础17-基于波士顿房价(Boston House Price)数据集训练模型的整个过程讲解

机器学习是一项经验技能&#xff0c;实践是掌握机器学习、提高利用机器学习 解决问题的能力的有效方法之一。那么如何通过机器学习来解决问题呢&#xff1f; 本节将通过一个实例来一步一步地介绍一个回归问题。 本章主要介绍以下内容&#xff1a; 如何端到端地完成一个回归问题…

笔试题目回忆

&#xff08;1&#xff09;给出n,k&#xff0c;n表示数组个数&#xff0c;k表示要剔除的个数&#xff0c;接下来n个数为数组元素&#xff0c;求剔除k个数之后&#xff0c;其他所有数互为倍数&#xff0c;每个数最多剔除一次。 未检测代码&#xff0c;超时。 #include <ios…

detour编译问题及导入visual studio

Detours是经过微软认证的一个开源Hook库&#xff0c;Detours在GitHub上&#xff0c;网址为 https://github.com/Microsoft/Detours 注意版本不一样的话也是会出问题的&#xff0c;因为我之前是vs2022的所以之前的detours.lib不能使用&#xff0c;必须用对应版本的x64 Native To…

Blender 围绕自身的原点旋转与游标旋转

默认情况下的旋转是&#xff0c;R后旋转是物体自身的原点旋转 可以修改为围绕游标旋转&#xff0c;通过旋转R时 局部与全局坐标 全局的坐标不会变 局部的会随着物体的旋转变化 如果平稳时GZZ会在全局到局部坐标之间切换 或在局部到全局之间的切换 学习视频&#xff1a;【基础…

浅谈 Android Binder 监控方案

在 Android 应用开发中&#xff0c;Binder 可以说是使用最为普遍的 IPC 机制了。我们考虑监控 Binder 这一 IPC 机制&#xff0c;一般是出于以下两个目的&#xff1a; 卡顿优化&#xff1a;IPC 流程完整链路较长&#xff0c;且依赖于其他进程&#xff0c;耗时不可控&#xff0…

【Unity】常见的角色移动旋转

在Unity 3D游戏引擎中&#xff0c;可以使用不同的方式对物体进行旋转。以下是几种常见的旋转方式&#xff1a; 欧拉角&#xff08;Euler Angles&#xff09;&#xff1a;欧拉角是一种常用的旋转表示方法&#xff0c;通过绕物体的 X、Y 和 Z 轴的旋转角度来描述物体的旋转。在Un…

opencv入门-Opencv原理以及Opencv-Python安装

图像的表示 1&#xff0c;位数 计算机采用0/1编码的系统&#xff0c;数字图像也是0/1来记录信息&#xff0c;图像都是8位数图像&#xff0c;包含0~255灰度&#xff0c; 其中0代表最黑&#xff0c;1代表最白 3&#xff0c; 4&#xff0c;OpenCV部署方法 安装OpenCV之前…

HTML+JavaScript+CSS DIY 分隔条splitter

一、需求分析 现在电脑的屏幕越来越大&#xff0c;为了利用好宽屏&#xff0c;我们在设计系统UI时喜欢在左侧放个菜单或选项面板&#xff0c;在右边显示与菜单或选项对应的内容&#xff0c;两者之间用分隔条splitter来间隔&#xff0c;并可以通过拖动分隔条splitter来动态调研…

表白墙程序

目录 一、页面代码部分 二、设计程序 二、实现 doPost​编辑 三、实现 doGet 四、前端代码部分 五、使用数据库存储数据 一、页面代码部分 在之前的一篇博客中&#xff0c;已经写过了表白墙的页面代码实现&#xff0c;这里就不再重复了 页面代码如下&#xff1a; <!…

springboot配置ym管理各种日记(log)

1&#xff1a;yml配置mybatis_plus默认日记框架 mybatis-plus:#这个作用是扫描xml文件生效可以和mapper接口文件使用&#xff0c;#如果不加这个,就无法使用xml里面的sql语句#启动类加了MapperScan是扫描指定包下mapper接口生效&#xff0c;如果不用MapperScan可以在每一个mapp…

[贪心] 拼接最小数

这道题思路并不难&#xff0c;我主要想学习其一些对于字符串的处理。 代码如下&#xff1a; #include <iostream> #include <string> #include <algorithm> using namespace std;const int MAXN 10000; string nums[MAXN];bool cmp(string a, string b) {…

创建ffmpeg vs2019工程

0 写在前面 本文主要参考链接&#xff1a;https://www.cnblogs.com/suiyek/p/15669562.html 感谢作者的付出&#xff1b; 1 目录结构 2 下载yasm和nasm 如果自己在安装VS2019等IDE的时候已经安装了它们&#xff0c;则不用再单独进行安装&#xff0c;比如我这边已经安装了&a…

【Redis从头学-完结】Redis全景思维导图一览!耗时半个月专为Redis初学者打造!

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;CSDN实力新星&#xff0c;后端开发两年经验&#xff0c;曾担任甲方技术代表&#xff0c;业余独自创办智源恩创网络科技工作室。会点点Java相关技术栈、帆软报表、低代码平台快速开…

keras深度学习框架通过简单神经网络实现手写数字识别

背景 keras深度学习框架&#xff0c;并不是一个独立的深度学习框架&#xff0c;它后台依赖tensorflow或者theano。大部分开发者应该使用的是tensorflow。keras可以很方便的像搭积木一样根据模型搭出我们需要的神经网络&#xff0c;然后进行编译&#xff0c;训练&#xff0c;测试…

从AD迁移至AAD,看体外诊断领军企业如何用网络准入方案提升内网安全基线

摘要&#xff1a; 某医用电子跨国集团中国分支机构在由AD向AzureAD Global迁移时&#xff0c;创新使用宁盾网络准入&#xff0c;串联起上海、北京、无锡等国内多个职场与海外总部,实现平滑、稳定、全程无感知的无密码认证入网体验&#xff0c;并通过合规基线检查&#xff0c;确…