【简写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, createTime
        FROM user
        where 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配置文件中初始化Configuration
    super(new Configuration());
    // 2. dom4j 处理 xml
    SAXReader 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, createTime
        FROM user
        where 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中获取SqlSession
        Reader 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, createTime
        FROM user
        where 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大模型

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vSphere 8考试认证题库 2024最新(VCP 8.0版本)

VMware VCP-DCV&#xff08;2V0-21.23&#xff09;认证考试题库&#xff0c;已全部更新&#xff0c;答案已经完成校对&#xff0c;完整题库请扫描上方二维码访问。正常考可以考到450分以上&#xff08;满分500分&#xff0c;300分通过&#xff09; An administrator is tasked …

HPE ProLiant MicroServer Gen8驱动程序下载(windows)

记录下&#xff0c;以方便需要重装系统时将驱动更新到最后版本。 共有下面设备有适用的驱动可用&#xff1a; 1、系统管理&#xff1a; iLO 4 Channel Interface Driver for Windows Server 2016 下面这个驱动&#xff0c;安装后不知道有什么用 iLO 3/4 Management Control…

基于springboot+vue实现物资仓储物流管理系统项目【项目源码+论文说明】计算机毕业设计

基于springbootvue实现物资仓储物流管理系统演示 摘要 随着我国经济及产业化结构的持续升级&#xff0c;越来越多的企业借助信息化及互联网平台实现了技术的创新以及竞争力的提升&#xff0c;在电子经济的影响下仓储物流业务也获得了更多的关注度&#xff0c;利用系统平台实现…

【软考】设计模式之享元模式

目录 1. 说明2. 应用场景3. 结构图4. 构成5. 适用性6. java示例 1. 说明 1.享元设计模式&#xff08;Flyweight Design Pattern&#xff09;是一种常见的软件设计模式2.属于结构型设计模式&#xff0c;对象结构型模式3.目的&#xff1a;运用共享技术有效地支持大量细粒度的对象…

【Wireshark傻瓜式安装,Wireshark使用过滤条件】

Wireshark傻瓜式安装&#xff0c;Wireshark使用过滤条件 安装使用wireshark过滤器表达式的规则1.抓包过滤器语法和实例&#xff08;1&#xff09;协议过滤&#xff08;2&#xff09;IP过滤&#xff08;3&#xff09;端口过滤&#xff08;4&#xff09;逻辑运算符&&与、…

后端传给前端的时间字段前端显示不正确

具体问题是什么呢&#xff0c;就比如我后段有一个字段是TimeStamp类型&#xff0c;从数据库中查出数据是下面的样式&#xff1a; 但是前端显示的是下面的格式&#xff1a; 这个的解决方法还是挺多的&#xff0c;那接下来具体来看看吧~ 第一种&#xff1a; 在application.prop…

【数据结构】数组、双链表代码实现

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

迁移篇 | MatrixOne与MySQL全面对比

Part 1 迁移背景 Skyable 自研了物联网私有云平台用于 IoT 设备的数据上报和协议解析&#xff0c;由于管理设备数量的增加导致设备上报的数据量越来越大&#xff0c;架构中原使用的 MySQL 数据库&#xff08;分库分表&#xff09;的部分业务在对设备上报信息进行相关的查询时&…

ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling

ChatGPT 结合实际地图实现问答式地图检索功能基于Function calling ChatGPT结合实际业务&#xff0c;主要是研发多函数调用&#xff08;Function Calling&#xff09;功能模块&#xff0c;将自定义函数通过ChatGPT 问答结果&#xff0c;实现对应函数执行&#xff0c;再次将结果…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:文本通用)

文本通用属性目前只针对包含文本元素的组件&#xff0c;设置文本样式。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 属性 名称参数类型描述fontColorResourceColor设置字体颜色。 从API version 9开…

VBA更新xlOLELinks链接的值

xlOLELinks是在Excel文档中插入对象的链接&#xff0c;该链接能够显示被插入文档的数据&#xff0c;通常情况下链接的数值会自动更新&#xff0c;但有时更新也会不及时或失效&#xff0c;这时就需要手动更新&#xff0c;如下图&#xff1a; 以插入Word文档为例&#xff0c;使用…

【漏洞复现】Laykefu客服系统任意文件上传

漏洞描述 Laykefu客服系统/admin/users/upavatar.html接口处存在文件上传漏洞,而且当请求中Cookie中的”user_name“不为空时即可绕过登录系统后台,未经身份验证的攻击者可利用此问题,上传后门文件,获取服务器权限。 免责声明 技术文章仅供参考,任何个人和组织使用网络…

js【深度解析】代码的执行顺序

代码的分类 我们将每一句要执行的 js 代码当做一个任务&#xff0c;则 js 代码可以按照其执行方式的不同&#xff0c;按下图分类 同步任务&#xff1a;立即执行的代码异步任务&#xff1a;延迟执行的代码 微任务&#xff1a;被放入微任务队列&#xff08;micro task queue&…

【记录37】VueBaiduMap 踩坑一

截图 错误 Error in callback for watcher “position.lng”: “TypeError: Cannot read properties of undefined (reading ‘setPosition’)” 解释 回调观察程序“content”时出错&#xff1a;“TypeError:无法读取未定义的属性&#xff08;读取’setContent’&#xff09;”…

设计模式-行为型模式-模版方法模式

模板方法模式&#xff0c;定义一个操作中的算法的骨架&#xff0c;而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。[DP] 模板方法模式是通过把不变行为搬移到超类&#xff0c;去除子类中的重复代码来体现它的优势。 //首…

L-2:插松枝(Python)

作者 陈越 单位 浙江大学 人造松枝加工场的工人需要将各种尺寸的塑料松针插到松枝干上&#xff0c;做成大大小小的松枝。他们的工作流程&#xff08;并不&#xff09;是这样的&#xff1a; 每人手边有一只小盒子&#xff0c;初始状态为空。每人面前有用不完的松枝干和一个推送…

《汇编语言》第3版(王爽)实验9

第9章 实验9 编程&#xff1a;在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串 ‘welcome to masm!’ assume cs:code,ds:datadata segmentdb welcome to masm!,0 data endscode segmentstart:mov ax,data mov ds,ax ;ds指向data段mov ax,0B800H ;显存空间从B800H…

LeetCode_24_中等_两两交换链表中的节点

文章目录 1. 题目2. 思路及代码实现&#xff08;Python&#xff09;2.1 递归2.2 迭代 1. 题目 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换…

windows11编译FFmpeg源码完整步骤

1.安装MSYS2 下载并安装MSYS2 安装GCC GCC安装成功 克隆FFmpeg源码 打开MSYS2终端并进入ffmpeg文件夹,然后输入./configure回车开始生成makefile

JavaEE——简单认识JS(Web API)

文章目录 一、认识什么是 WebAPI二、认识事件三、操作元素1. innerHTML 属性2. 获取 / 修改元素内容3. 获取 / 修改 元素属性4. 获取 / 修改 表单元素属性5. 获取 / 修改 样式属性6. 创建 / 删除元素 一、认识什么是 WebAPI 1.什么是API 在我们了解 WebAPI 之前&#xff0c;我们…