当前位置: 首页 > news >正文

【简写Mybatis】03-Mapper xml的注册和使用

前言

在学习MyBatis源码文章中,斗胆想将其讲明白;故有此文章,如有问题,不吝指教!

注意:

学习源码一定一定不要太关注代码的编写,而是注意代码实现思想;

通过设问方式来体现代码中的思想;方法:5W+1H

源代码:https://gitee.com/xbhog/mybatis-xbhoghttps://github.com/xbhog/mybatis-xbhog;交个朋友,欢迎star。
也可关注笔者公众号:

回顾&分析

上一局实现【简写Mybatis】02-注册机的实现以及SqlSession处理;主要是为了完善Mapper的注册方式以及SqlSession规范的流程;

上一局的测试类如下;在使用上还是比我们平常熟悉的Mybatis要差好多,比如没有Mapper配置文件、Mapper映射文件、没有针对数据库操作等。

/*** Unit test for simple App.*/
public class AppTest extends TestCase {/*** Rigourous Test :-)*/public void testApp() {MapperRegistry mapperRegistry = new MapperRegistry();mapperRegistry.addMapper("com.xbhog");DefaultSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(mapperRegistry);SqlSession sqlSession = sqlSessionFactory.openSession();IUserDao user = sqlSession.getMapper(IUserDao.class);String userName = user.getUserName("xbhog");System.out.println("输出的信息:"+userName);}
}

目的

  1. XML的解析和读取
  2. 封装XML数据
  3. 封装配置类
  4. 封装MapperRegistry和SqlSessionFactory,设置统一入口
  5. 封装Mapper文件中方法的执行操作

实现

XML的解析和读取

引入相关的Maven依赖:

<dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</version>
</dependency>
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.0</version>
</dependency>

解析的相关代码如下:已有注释

// xml文件内容转换为字符串流
Reader reader = ResourceUtil.getUtf8Reader("mybatis-config-datasource.xml");// 从字符串流中读取并创建XML Document对象
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new InputSource(reader));// 获取XML文档的根元素
Element rootElement = document.getRootElement();// 获取根元素下的“mappers”子元素
Element mappersElement = rootElement.element("mappers");// 遍历所有“mapper”子元素
List<Element> mapperElements = mappersElement.elements("mapper");
for (Element mapperElement : mapperElements) {// 获取当前“mapper”元素的“resource”属性值String mapperResource = mapperElement.attributeValue("resource");System.out.println("正在查看资源:" + mapperResource);// 依据“resource”属性加载对应的XML文件内容为字符串流Reader mapperReader = ResourceUtil.getUtf8Reader(mapperResource);// 创建新的SAXReader实例以读取mapper文件中的XML内容SAXReader saxReaderForMapper = new SAXReader();// 从mapper的字符串流中创建新的Document对象Document mapperDocument = saxReaderForMapper.read(new InputSource(mapperReader));// 获取mapper文件的根元素Element mapperRootElement = mapperDocument.getRootElement();// 遍历mapper文件中所有的“select”元素List<Element> selectNodes = mapperRootElement.elements("select");for (Element selectElement : selectNodes) {// 获取“select”元素的各个属性值String selectId = selectElement.attributeValue("id");String parameterType = selectElement.attributeValue("parameterType");String resultType = selectElement.attributeValue("resultType");String sqlStatement = selectElement.getText();// 输出“select”元素的属性及SQL语句System.out.println("相关SQL映射信息:ID=" + selectId + ";参数类型=" + parameterType +";结果类型=" + resultType + ";SQL语句=" + sqlStatement);}
}

作用是从一个主配置文件(“mybatis-config-datasource.xml”)中读取到多个mapper资源配置,并逐个加载这些mapper资源文件。针对每个mapper文件,进一步提取出其中所有的SQL select 映射定义,包括其ID、参数类型、结果类型以及具体的SQL语句;这里是将这些信息打印出来,后续将保存到实体或者配置方便后续使用。

看下解析的效果:

正在查看资源:mapper/User_Mapper.xml
相关SQL映射信息:ID=queryUserInfoById;参数类型=java.lang.Long;结果类型=com.xbhog.User;SQL语句=SELECT id, userId, userHead, createTimeFROM userwhere id = #{id}

XML配置构建器

这部分采用建造者模式实现XML的配置,该模式比较适合基本物料不变,而其组合经常发生变化的场景;核心目的是将复杂对象的构建过程与它的表示分离。

建造者分为一下几个角色:

  1. 抽象建造者:定义了创建产品对象的各个部分的方法(比如零件或组件),一般会有一个方法来获取最终的复杂产品。
  2. 具体建造者:实现了抽象建造者接口,完成每个部分的具体构造和装配方法,提供构造过程的具体实现。
  3. 产品(复杂对象):是被构建的复杂对象,包含了多个组成部件。具体建造者创建产品的各个部件并最终组合成完整的产品。
抽象建造者

按照上述的定义以及具体我们想实现的业务可以拆分成两个操作:

  1. 初始化XML字符流,转换成Doc供后续使用
  2. 解析完的DOC,实体类进行保存,并且需要一个出口进行获得
public abstract class BaseBuilder {protected final Configuration configuration;public BaseBuilder(Configuration configuration) {this.configuration = configuration;}//只保证外部能够获得所有的信息,具体的配置在子类中赋值public Configuration getConfiguration() {return configuration;}}

BaseBuilder提供了构建Configuration对象所需要的基本方法和属性,并且也对其子类有着通用的构建逻辑。

建造者实现

初始化XML字符流,转换Doc:

public XmlConfigBuilder(Reader reader) {//在处理XML配置文件中初始化Configurationsuper(new Configuration());// 2. dom4j 处理 xmlSAXReader saxReader = new SAXReader();try {Document document = saxReader.read(new InputSource(reader));root = document.getRootElement();} catch (DocumentException e) {e.printStackTrace();}
}

初始化XML字符流在上一小节已经解决了,在正常Myabtis中的Sql是有占位符或者拼接符;并将相关元素的保存到mappedStatement(映射器语句类)中;

映射类语句类的编写:

/*** @author xbhog* @describe: 用于封装MyBatis中映射SQL语句的相关信息,包括配置信息、SQL类型、参数类型、结果类型、SQL语句以及动态参数映射等。* @date 2024/3/2*/
public class MappedStatement {/*** 配置对象,包含MyBatis运行所需的环境、数据库映射等全局配置信息。*/private Configuration configuration;/*** 映射ID,唯一标识一个MappedStatement,通常对应XML文件中的<mappedStatement>标签的id属性。*/private String id;/*** SQL命令类型,如INSERT、UPDATE、SELECT或DELETE等。*/private SqlCommandType sqlCommandType;/*** 参数类型,对应于传入SQL语句的参数类的全限定名。*/private String parameterType;/*** 结果类型,对应于SQL查询结果映射到的Java类型的全限定名。*/private String resultType;/*** SQL语句,可能是预编译的静态SQL或带有占位符的动态SQL。*/private String sql;/*** 动态参数映射集合,键为参数的位置(从0开始计数),值为参数的名称。*/private Map<Integer, String> parameter;/*** 空构造器,主要用于反射创建实例。*/public MappedStatement() {}/*** 内部静态嵌套类Builder,遵循建造者设计模式,用于构建MappedStatement实例。*/public static class Builder {/*** 储存待构建的MappedStatement对象引用。*/private MappedStatement mappedStatement = new MappedStatement();/*** 初始化Builder对象,并设置MappedStatement的所有必要属性。** @param configuration MyBatis的全局配置对象* @param id 映射ID* @param sqlCommandType SQL命令类型* @param parameterType 参数类型全限定名* @param resultType 结果类型全限定名* @param sql SQL语句* @param parameter 动态参数映射集合*/public Builder(Configuration configuration, String id, SqlCommandType sqlCommandType, String parameterType, String resultType, String sql, Map<Integer, String> parameter) {mappedStatement.configuration = configuration;mappedStatement.id = id;mappedStatement.sqlCommandType = sqlCommandType;mappedStatement.parameterType = parameterType;mappedStatement.resultType = resultType;mappedStatement.sql = sql;mappedStatement.parameter = parameter;}/*** 完成构建并返回MappedStatement实例,同时进行必要的非空断言检查。** @return 已经填充好所有必要属性的MappedStatement实例*/public MappedStatement build() {assert mappedStatement.configuration != null : "Configuration must not be null!";assert mappedStatement.id != null : "ID must not be null!";return mappedStatement;}}
}

存储部分设置完成,我们处理下select的操作:

// 遍历所有“mapper”子元素
List<Element> mapperElements = mappers.elements("mapper");
for (Element mapperElement : mapperElements) {......//命名空间String namespace = mapperRootElement.attributeValue("namespace");// 遍历mapper文件中所有的“select”元素(暂时只有这一个操作)List<Element> selectNodes = mapperRootElement.elements("select");for (Element selectElement : selectNodes) {// 获取“select”元素的各个属性值String selectId = selectElement.attributeValue("id");String parameterType = selectElement.attributeValue("parameterType");String resultType = selectElement.attributeValue("resultType");String sql = selectElement.getText();Map<Integer, String> parameter = new HashMap<>();Pattern pattern = Pattern.compile("(#\\{(.*?)})");Matcher matcher = pattern.matcher(sql);for(int i = 1; matcher.find(); i++){String group1 = matcher.group(1);String group2 = matcher.group(2);log.info("匹配出来的信息为:{}:{}", group1, group2);parameter.put(i, group2);//替换占位符sql = sql.replace(group1,"?");}//获取全路径String msId = namespace + "." + selectId;//获取sql方法(select.....)String nodeName = selectElement.getName();//替换,保持大小写一致SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));//保存MappedStatement mappedStatement = new MappedStatement.Builder(configuration, msId,sqlCommandType, parameterType, resultType, sql, parameter).build();//配置文件设置()configuration.addMappedStatement(mappedStatement);}// 注册Mapper映射器configuration.addMapper(Class.forName(namespace));
}

可以发现上述的代码中出来在XML的解析以及映射器属性的保存外,还有新加的configuration类。在mybatis中Configuration是一个重量级的核心配置类,几乎包含了Mybatis运行的所有的配置信息。

在正式Mybatis中Configuration的作用:

  1. 存储全局配置信息:如数据库连接信息(driver、url、username、password)、事务管理器设置、映射文件位置、自定义类型处理器、日志工厂等。
  2. 管理映射资源:存储并管理所有 <mapper> 标签对应的 MappedStatement 对象,这些对象包含了SQL语句、参数类型、结果类型、动态SQL解析器等信息。(本次处理的重点)
  3. 提供动态SQL解析:基于Configuration对象,MyBatis能解析包含动态元素的SQL语句。(本次处理的重点)
  4. 支持延迟加载和缓存策略:在Configuration中可以配置二级缓存、全局的缓存开关、延迟加载策略等。
  5. 提供内置对象工厂:Configuration对象维护了一个对象工厂,用于在运行时创建诸如ParameterHandler、ResultSetHandler、StatementHandler等核心对象。

本节处理流程:

img

这得看下SqlSessionFatoryBuilder入口类的实现:

public class SqlSessionFactoryBuilder {public SqlSessionFactory build(Reader reader) {XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(reader);return build(xmlConfigBuilder.parse());}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}
}

先说结论:configuration类本身不是单例类,相反是Mybatis通过 SqlSessionFactoryBuilder 来确保整个应用中只有一个 Configuration 实例,Configuration在****SqlSessionFactoryBuilder构建阶段完成实例化的操作。

一旦 SqlSessionFactory 被创建,SqlSessionFatoryBuilder 的使命就完成了,之后 SqlSessionFactory 就会被用来创建 SqlSession 实例,进而执行 SQL 语句、管理事务和进行其他数据库操作。由于 SqlSessionFatoryBuilder 的任务完成后就可以丢弃,因此通常采取即用即抛的原则,正是由于SqlSessionFatoryBuilder 的这种原则,保证了Configuration 有了单例的特性。

Mapper文件中方法的执行操作封装

看下之前的invoke方法的实现:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if(Object.class.equals(method.getDeclaringClass())){return method.invoke(this,args);}else{//todo 具体接口实现的方式return sqlSession.selectOne(method.getName(), args);}
}

在上一局中我们在invoke方法中指定了sql的处理类型,基本操作都知道肯定不会有一种类型;为了读者能更好的理解,这里先看下上一局和这一局代码的新旧流程对比。

对比范围:

└─src ├─main │ └─java │   └─com │     └─xbhog │       ├─binding │       │ ├─MapperMethod.java(新增) │       │ ├─MapperProxy.java │       │ ├─MapperProxyFactory.java │       │ └─MapperRegistry.java 

该流程处理代码流程不变,细节地方会有Configuration处理,详细请看代码操作。

变化比较大的是在MapperProxyinvoke方法的执行操作上;

这个时序图是MapperMethod类流程;

简单介绍下功能

  1. 在mapperMethod初始化前会先从methodCache中进行查询,存在返回,不存在构建完并返回,类似于缓存。
  2. mapperMethod初始化内包含了SqlCommand指令的构建,采用的模式还是建造者模式,内置方法名和方法类型属性,通过接口名和方法名从Configuration获取mappedStatements构建属性值。
  3. 其他操作就是把crud进行分开判断并执行。

到此所有涉及到的操作,都或多或少的写到了,接下来进行测试。

测试

环境构建

│  └─test
│      ├─java
│      │  └─com
│      │      └─xbhog
│      │              AppTest.java
│      │              IUserDao.java
│      │              User.java
│      │              
│      └─resources
│          │  mybatis-config-datasource.xml
│          │  
│          └─mapper
│                  User_Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><mappers><mapper resource="mapper/User_Mapper.xml"/></mappers></configuration>
<mapper namespace="com.xbhog.IUserDao"><select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.xbhog.User">SELECT id, userId, userHead, createTimeFROM userwhere id = #{id}</select></mapper>
public class AppTest extends TestCase {private Logger logger = LoggerFactory.getLogger(AppTest.class);/*** Rigourous Test :-)*/public void testApp() throws Exception {// 1. 从SqlSessionFactory中获取SqlSessionReader reader = ResourceUtil.getUtf8Reader("mybatis-config-datasource.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);SqlSession sqlSession = sqlSessionFactory.openSession();// 2. 获取映射器对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 3. 测试验证String res = userDao.queryUserInfoById("10001");logger.info("测试结果:{}", res);}
}

结果如下:

正在查看资源:mapper/User_Mapper.xml
11:55:58.436 [main] INFO  c.xbhog.builder.xml.XmlConfigBuilder - 匹配出来的信息为:#{id}:id
11:55:58.456 [main] INFO  com.xbhog.AppTest - 测试结果:你被代理了!方法:com.xbhog.IUserDao.queryUserInfoById 入参:[Ljava.lang.Object;@71c7db30,待执行的SQl:SELECT id, userId, userHead, createTimeFROM userwhere id = ?

总结

整个流程大致是从配置文件出发,经过 SqlSessionFactoryBuilderXmlConfigBuilder 构建并填充 Configuration;然后 Configuration 被用来创建 SqlSessionFactory,进而生产 SqlSession;同时,Configuration 中还维护了所有 MappedStatement 以及 Mapper 相关的注册信息,确保 SqlSession 在处理数据库操作请求时能够找到正确的映射关系并执行相应的方法。对于 Mapper 接口,它们通过 MapperRegistryMapperProxyFactoryMapperProxy 构成了一个面向接口编程的持久层访问机制。

类名类的作用
SqlSessionFactoryBuilder这是一个工具类,用于从配置信息(如XML配置文件或预定义的Configuration对象)构建SqlSessionFactory实例。它接收配置输入,解析并验证配置,然后创建并初始化SqlSessionFactory
SqlSessionFactory可以理解为SqlSession的工厂,它持有MyBatis的核心配置信息(由Configuration对象提供)。每个MyBatis应用的核心就是SqlSessionFactory实例,它负责创建SqlSession对象。SqlSessionFactory通常是线程安全的,可以被多个线程共享,并在整个应用生命周期内保持。
SqlSession是MyBatis执行数据库操作的主要入口点,它提供了CRUD(增删改查)等各种数据库操作方法。每个SqlSession对象都与一个数据库连接(Connection)关联,且不是线程安全的,通常在一个请求或操作范围内使用。
Configuration这是MyBatis的全局配置容器,它存储了所有关于MyBatis的行为配置、所有映射器的注册信息、数据源、事务管理器、类型处理器等配置。SqlSessionFactoryBuilder正是根据Configuration的信息来构建SqlSessionFactory
DefaultSqlSessionFactory这是SqlSessionFactory的一个具体实现类,继承自SqlSessionFactory接口,它包含了创建和管理SqlSession的实际逻辑。
DefaultSqlSession这是SqlSession接口的默认实现,它封装了对数据库的CRUD操作以及对MappedStatement的访问。
SqlCommandType表示SQL命令的类型枚举,包括INSERT、UPDATE、DELETE、SELECT等,这是在MappedStatement中用来区分不同类型的SQL操作。
MappedStatement表示一个已经映射好的SQL语句及其相关配置,包括SQL类型、参数类型、结果类型、SQL语句本身以及可能的动态SQL节点和参数映射规则。
XmlConfigBuilder用于解析MyBatis的XML配置文件,将XML配置信息转换为Configuration对象。
BaseBuilder在MyBatis中,如果有多个Builder类有着相似的构建逻辑,可能会定义一个基类BaseBuilder,提取公共方法和属性,不过这里并未明确提到它在MyBatis中的具体实现。
MapperRegistry在MyBatis中,MapperRegistry是一个注册和管理所有Mapper接口的地方,它负责存储和查找Mapper接口对应的MapperProxyMapperMethod
MapperMethod表示在Mapper接口中定义的某个方法的具体实现,它封装了方法对应的SQL执行逻辑。
MapperProxyMyBatis利用Java动态代理机制,通过MapperProxy来实现代理Mapper接口的对象,当调用Mapper接口方法时,实际上是调用了MapperProxy中的方法,从而执行SQL操作。
MapperProxyFactory负责创建MapperProxy实例的工厂类,它根据Mapper接口生成对应的动态代理对象,使用户可以通过接口的方式来执行数据库操作,而不是直接与SqlSession打交道。每次需要新的Mapper代理对象时,都会通过MapperProxyFactory来创建。

学习&参考

https://mp.weixin.qq.com/s/C_bb9f1Hr9aJAvbmrDHHig

mybatis源码

AI大模型

相关文章:

【简写Mybatis】03-Mapper xml的注册和使用

前言 在学习MyBatis源码文章中&#xff0c;斗胆想将其讲明白&#xff1b;故有此文章&#xff0c;如有问题&#xff0c;不吝指教&#xff01; 注意&#xff1a; 学习源码一定一定不要太关注代码的编写&#xff0c;而是注意代码实现思想&#xff1b; 通过设问方式来体现代码中的…...

Vue源码系列讲解——指令篇【一】(自定义指令)

目录 1. 前言 2. 何时生效 3. 指令钩子函数 4. 如何生效 5. 总结 1. 前言 在Vue中&#xff0c;除了Vue本身为我们提供的一些内置指令之外&#xff0c;Vue还支持用户自定义指令。并且用户有两种定义指令的方式&#xff1a;一种是使用全局API——Vue.directive来定义全局指令…...

STM32(14)USART

USART:一种片上外设&#xff0c;用来实现串口通信&#xff0c;就是stm32内部的串口 USART简介 串并转换电路 串行通信和并行通信 串行&#xff1a;一根数据线&#xff0c;逐个比特位发送 为什么要串并转换 移位寄存器 USART的基本模型 通过查询SR&#xff08;状态寄存器&…...

作业 字符数组-统计和加密

字串中数字个数 描述 输入一行字符&#xff0c;统计出其中数字字符的个数。 输入 一行字符串&#xff0c;总长度不超过255。 输出 输出为1行&#xff0c;输出字符串里面数字字符的个数。 样例 #include <iostream> #include<string.h> using namespace std; int m…...

Codeforces Round 719 (Div. 3)除F2题外补题报告

Codeforces Round 719 Div. 3 除F2题外补题报告 得分情况补题情况错题分析C题题目大意初次思路正解思路正解代码错误原因 D题题目大意初次思路正解思路正解代码错误原因 E题题目大意初次思路正解思路正解代码 F1题题目大意正解思路正解代码 G题题目大意正解思路正解代码 得分情…...

docker本地搭建spark yarn hive环境

docker本地搭建spark yarn hive环境 前言软件版本准备工作使用说明构建基础镜像spark on yarn模式构建on-yarn镜像启动on-yarn集群手动方式自动方式 spark on yarn with hive(derby server)模式构建on-yarn-hive镜像启动on-yarn-hive集群手动方式自动方式 常用示例spark执行sh脚…...

每日学习笔记:C++ 11的Tuple

#include <tuple> Tuple介绍(不定数的值组--可理解为pair的升级版) 定义 创建 取值 初始化 获取tuple元素个数、获取tuple某元素类型、将2个tuple类型串接为1个新tuple类型...

MongoDB聚合运算符;$dateToParts

$dateToParts聚合运算符将日期表达式拆分成多个字段放在一个文档返回&#xff0c;属性有year、month、day、hour、minute、second和millisecond。如果iso8601属性设置为true&#xff0c;返回的各部分用ISO周日期返回&#xff0c;属性分别是&#xff1a;isoWeekYear、isoWeek、i…...

Spring MVC RequestMappingHandlerAdapter原理解析

在Spring MVC框架中&#xff0c;RequestMappingHandlerAdapter是一个核心的组件&#xff0c;负责将请求映射到具体的处理器方法上&#xff0c;并调用这些方法来处理请求。其中&#xff0c;invokeHandlerMethod方法是这个适配器中的一个关键方法&#xff0c;它负责实际调用处理器…...

反射整理学习

目录 1、反射介绍 2、反射API 2.1 获取类对应的字节码的对象&#xff08;三种&#xff09; 2.2 常用方法 3、反射的应用 3.1 创建 : 测试物料类 3.2 获取类对象 3.3 获取成员变量 3.4 通过字节码对象获取类的成员方法 3.5 通过字节码对象获取类的构造方法 4、创建对象…...

JavaScript 运算规则详解

在 JavaScript 中&#xff0c;运算规则是非常重要的基础知识&#xff0c;了解这些规则可以帮助我们正确地编写代码并避免一些常见的错误。本教程将详细介绍 JavaScript 中的各种运算规则&#xff0c;包括基本运算符、类型转换、运算优先级等内容。 1. 基本运算符 JavaScript …...

C++篇 语 句

到目前为止&#xff0c;我们只见过两种语句&#xff1a; return 语句和表达式语句。根据语句对执行顺 序的影响&#xff0c;C 语言其余语句大多属于以下 3 大类。 选择语句&#xff1a; if 语句和 switch 语句。循环语句&#xff1a; while 语句&#xff0c; do...while 语句和…...

简洁的在线观影开源项目

公众号&#xff1a;【可乐前端】&#xff0c;每天3分钟学习一个优秀的开源项目&#xff0c;分享web面试与实战知识。 每天3分钟开源 hi&#xff0c;这里是每天3分钟开源&#xff0c;很高兴又跟大家见面了&#xff0c;今天介绍的开源项目简介如下&#xff1a; 仓库名&#xff1…...

VB超级模块函数VB读写记事本-防止乱码支持UTF-8和GB2312编码

Private Sub Command1_Click() Writein “C:\Users\Administrator\Desktop\1.txt”, “文本文内容” End Sub Private Sub Form_Load() Text1 ReadANSI(“C:\Users\Administrator\Desktop\1.txt”) Text2 ReadUTF8(“C:\Users\Administrator\Desktop\1.txt”) End Sub 写入…...

XSS靶场-DOM型初级关卡

一、环境 XSS靶场 二、闯关 1、第一关 先看源码 使用DOM型&#xff0c;获取h2标签&#xff0c;使用innerHTML将内容插入到h2中 我们直接插入<script>标签试一下 明显插入到h2标签中了&#xff0c;为什么不显示呢&#xff1f;看一下官方文档 尽管插入进去了&#xff0…...

【嵌入式高级C语言】10:C语言文件

文章目录 1 文件的概述1.1 文件分类&#xff08;存储介质&#xff09;1.2 磁盘文件分类&#xff08;存储方式&#xff09;1.3 二进制文件和文本文件的区别 2 文件缓冲区3 文件指针4 文件的API4.1 打开文件4.2 关闭文件4.3 重新定位流4.3.1 fseek4.3.2 ftell4.3.3 rewind 4.4 字…...

创建数据表

Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 如果要进行数据表的创建 create table 表名称 (列名称 类型 [DEFAULT 默认值 ] ,列名称 类型 [DEFAULT 默认值 ] ,列名称 类型 [DEFAULT 默认值 ] ,...列名称 类型 [DEFAULT 默认值 ] )…...

C语言字符串型常量

在C语言中&#xff0c;字符串型常量是由一系列字符组成的常量。字符串常量在C中以双引号&#xff08;"&#xff09;括起来&#xff0c;例如&#xff1a;“Hello, World!”。字符串常量在C中是不可变的&#xff0c;也就是说&#xff0c;一旦定义&#xff0c;就不能修改其内…...

计算机网络 八股

计算机网络体系结构 OSI&#xff1a;物理层、数据链路层、网络层、运输层、会话层、表示层、应用层...

深入了解 Jetpack Compose 中的 Modifier

Jetpack Compose 是 Android 中用于构建用户界面的现代化工具包。其中&#xff0c;Modifier 是一个非常重要的概念&#xff0c;它允许我们对 UI 组件进行各种样式和布局的调整。在本篇博客中&#xff0c;我们将深入了解 Modifier&#xff0c;以及如何在 Compose 中使用它。 什…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

pycharm 设置环境出错

pycharm 设置环境出错 pycharm 新建项目&#xff0c;设置虚拟环境&#xff0c;出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中&#xff0c;API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关&#xff0c;Kong凭借其插件化架构…...