Mr. Cappuccino的第55杯咖啡——Mybatis一级缓存二级缓存

news2024/9/21 20:50:36

Mybatis一级缓存&二级缓存

    • 概述
    • 一级缓存
      • 特点
      • 演示前准备
      • 效果演示
        • 在同一个SqlSession中
        • 在不同的SqlSession中
      • 源代码
      • 怎么禁止使用一级缓存
      • 一级缓存在什么情况下会被清除
    • 二级缓存
      • 特点
      • 演示前准备
      • 效果演示
        • 在不同的SqlSession中
      • 源代码
      • 怎么关闭二级缓存
    • 一级缓存(Spring整合Mybatis)
      • 演示前准备
      • 效果演示
        • 不开启事务,调用多次接口
        • 开启事务,调用多次接口
        • 不开启事务,接口中多次调用查询方法
        • 开启事务,接口中多次调用查询方法
        • 总结
      • 源代码

概述

缓存越小,查询速度越快,缓存数据越少
缓存越大,查询速度越慢,缓存数据越多

在多级缓存中,一般常见的是先查询一级缓存,再查询二级缓存,但在Mybatis中是先查询二级缓存,再查询一级缓存。

在Mybatis中,BaseExecutor属于一级缓存执行器,CachingExecutor属于二级缓存执行器,二者采用了装饰器设计模式。

一级缓存:默认情况下一级缓存是开启的,而且是不能关闭的,一级缓存是指SqlSession级别的缓存,当在同一个SqlSession中使用相同的SQL语句进行查询时,第二次以及之后的查询都不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存1024条SQL。
二级缓存:二级缓存是指可以跨SqlSession的缓存。是mapper级别的缓存,对于mapper级别的缓存不同的SqlSession是可以共享的,需要额外整合第三方缓存,例如Redis、MongoDB、oscache、ehcache等。

注:本文代码演示基于《Mybatis环境搭建与使用》中的“基于XML方式-mapper代理开发”的代码进行调整。

一级缓存

特点

一级缓存也叫本地缓存,在Mybatis中,一级缓存是在会话层面(SqlSession)实现的,这就说明一级缓存的作用范围只能在同一个SqlSession中,在多个不同的SqlSession中是无效的。

在Mybatis中,一级缓存是默认开启的,不需要任何额外的配置。

演示前准备

为了能够看到演示的效果,需要在mybatis-config.xml文件中加上以下配置

<settings>
    <!-- 打印sql日志 -->
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

在这里插入图片描述

效果演示

在同一个SqlSession中

MybatisTest03.java

package com.mybatis.test;

import com.mybatis.entity.UserEntity;
import com.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author honey
 * @date 2023-08-01 16:23:53
 */
public class MybatisTest03 {

    public static void main(String[] args) throws IOException {
        // 1.读取加载mybatis-config.xml(数据源、mybatis等配置)
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 2.获取sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 3.根据mapper(namespace="UserMapper全限定名" + id="listUser")执行sql语句,并将查询到的数据映射成对象(orm)
        UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);
        System.out.println("【一级缓存-在同一个SqlSession中】第一次查询");
        List<UserEntity> list1 = mapper1.listUser();
        System.out.println("list1:" + list1);

        UserMapper mapper2 = sqlSession.getMapper(UserMapper.class);
        System.out.println("【一级缓存-在同一个SqlSession中】第二次查询");
        List<UserEntity> list2 = mapper2.listUser();
        System.out.println("list2:" + list2);

        sqlSession.close();
    }
}

运行上面的代码可以看到,在同一个SqlSession中,第二次查询是没有去查询数据库的,而是直接读取的缓存数据。

在这里插入图片描述

源码Debug分析

BaseExecutor.java

在这里插入图片描述
在这里插入图片描述

在不同的SqlSession中

MybatisTest04.java

package com.mybatis.test;

import com.mybatis.entity.UserEntity;
import com.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author honey
 * @date 2023-08-01 16:23:53
 */
public class MybatisTest04 {

    public static void main(String[] args) throws IOException {
        // 1.读取加载mybatis-config.xml(数据源、mybatis等配置)
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 2.获取sqlSession
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        // 3.根据mapper(namespace="UserMapper全限定名" + id="listUser")执行sql语句,并将查询到的数据映射成对象(orm)
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        System.out.println("【一级缓存-在不同的SqlSession中】第一次查询");
        List<UserEntity> list1 = mapper1.listUser();
        System.out.println("list1:" + list1);

        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        System.out.println("【一级缓存-在不同的SqlSession中】第二次查询");
        List<UserEntity> list2 = mapper2.listUser();
        System.out.println("list2:" + list2);

        sqlSession1.close();
        sqlSession2.close();
    }
}

运行上面的代码可以看到,在不同的SqlSession中,两次查询都是查询的数据库,也就是说一级缓存并没有生效。

在这里插入图片描述

源代码

在这里插入图片描述
在这里插入图片描述

怎么禁止使用一级缓存

  1. 在SQL语句上加上随机生成的参数;(不推荐)
  2. 开启二级缓存;
  3. 使用SqlSession强制清除缓存;
  4. 每次查询都使用新的SqlSession;
  5. 通过配置清除缓存;

一级缓存在什么情况下会被清除

  1. 提交事务/回滚事务/强制清除缓存
sqlSession.commit();
sqlSession.rollback();
sqlSession.clearCache()

以提交事务为例,回滚事务/强制清除缓存同理

MybatisTest03.java

在这里插入图片描述

DefaultSqlSession.java

在这里插入图片描述

BaseExecutor.java

在这里插入图片描述
在这里插入图片描述

  1. 在执行insert、update、delete语句时

BaseExecutor.java

在这里插入图片描述

  1. 使用配置清除一级缓存
<!-- 设置一级缓存作用域 -->
<setting name="localCacheScope" value="STATEMENT"/>

mybatis-config.xml

在这里插入图片描述

BaseExecutor.java

在这里插入图片描述

二级缓存

特点

二级缓存是mapper级别的缓存,通过整合第三方缓存实现,二级缓存的作用范围可以在不同的SqlSession中。

在Mybatis中,二级缓存默认是开启的,但还需要做一些额外的配置才能生效。

演示前准备

在这里插入图片描述

  1. 启动Redis

在这里插入图片描述

  1. 添加pom依赖

pom.xml

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.0.1</version>
</dependency>
  1. 实现Cache类

RedisCache.java

package com.mybatis.cache;

import com.mybatis.utils.SerializeUtil;
import org.apache.ibatis.cache.Cache;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author honey
 * @date 2023-08-01 23:44:10
 */
public class RedisCache implements Cache {

    private final Jedis redisClient = createRedis();

    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private final String id;

    public RedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require an ID");
        }
        this.id = id;
    }


    @Override
    public String getId() {
        return id;
    }

    @Override
    public void putObject(Object key, Object value) {
        System.out.printf("【存入缓存数据】key:%s,value:%s%n", key, value);
        redisClient.set(SerializeUtil.serialize(key), SerializeUtil.serialize(value));
    }

    @Override
    public Object getObject(Object key) {
        byte[] bytes = redisClient.get(SerializeUtil.serialize(key));
        if (bytes == null) {
            return null;
        }
        Object value = SerializeUtil.deserialize(bytes);
        System.out.printf("【读取缓存数据】key:%s,value:%s%n", key, value);
        return value;
    }

    @Override
    public Object removeObject(Object key) {
        return redisClient.expire(String.valueOf(key), 0);
    }

    @Override
    public void clear() {
        redisClient.flushDB();
    }

    @Override
    public int getSize() {
        return Integer.parseInt(redisClient.dbSize().toString());
    }

    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }

    protected static Jedis createRedis() {
        JedisPool pool = new JedisPool("127.0.0.1", 6379);
        return pool.getResource();
    }
}

SerializeUtil.java

package com.mybatis.utils;

import java.io.*;

/**
 * @author honey
 * @date 2023-08-02 00:50:37
 */
public class SerializeUtil {

    public static byte[] serialize(Object object) {
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            // 序列化
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(oos);
            close(baos);
        }
        return null;
    }

    public static Object deserialize(byte[] bytes) {
        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
        try {
            // 反序列化
            bais = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(bais);
            close(ois);
        }
        return null;
    }

    /**
     * 关闭io流对象
     *
     * @param closeable closeable
     */
    public static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

注意:UserEntity需要实现序列化接口

UserEntity.java

在这里插入图片描述

  1. 添加配置(userMapper.xml)

userMapper.xml

<cache eviction="LRU" type="com.mybatis.cache.RedisCache"/>

在这里插入图片描述

效果演示

在不同的SqlSession中

MybatisTest05.java

package com.mybatis.test;

import com.mybatis.entity.UserEntity;
import com.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author honey
 * @date 2023-08-01 16:23:53
 */
public class MybatisTest05 {

    public static void main(String[] args) throws IOException {
        // 1.读取加载mybatis-config.xml(数据源、mybatis等配置)
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 2.获取sqlSession
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        // 3.根据mapper(namespace="UserMapper全限定名" + id="listUser")执行sql语句,并将查询到的数据映射成对象(orm)
        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
        System.out.println("【二级缓存-在不同的SqlSession中】第一次查询");
        List<UserEntity> list1 = mapper1.listUser();
        System.out.println("list1:" + list1);

        sqlSession1.close();

        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
        System.out.println("【二级缓存-在不同的SqlSession中】第二次查询");
        List<UserEntity> list2 = mapper2.listUser();
        System.out.println("list2:" + list2);

        sqlSession2.close();
    }
}

运行上面的代码可以看到,在不同的SqlSession中,第一次查询读取的是数据库中的数据,而第二次查询读取的是缓存中的数据。

在这里插入图片描述

注意:查询到的数据并不是在第一时间就存入缓存,而是在提交事务(sqlSession1.close())的时候才存入缓存。

在这里插入图片描述

源代码

CachingExecutor.java

在这里插入图片描述

在这里插入图片描述

TransactionalCacheManager.java

在这里插入图片描述

在这里插入图片描述


根据Cache(id=“mapper全限定名”)获取对应的TransactionalCache对象,并将数据临时存放在该对象中。

在这里插入图片描述


TransactionalCache.java

在这里插入图片描述

在执行sqlSession1.close()这行代码时,会将临时存放的数据存入缓存。

DefaultSqlSession.java

在这里插入图片描述

CachingExecutor.java

在这里插入图片描述

  1. 如果是提交事务,则会先将临时存放的数据存入缓存,再将临时存放的数据清空

TransactionalCacheManager.java

在这里插入图片描述

TransactionalCache.java

在这里插入图片描述

在这里插入图片描述

  1. 如果是回滚事务,则只会将临时存放的数据清空

TransactionalCacheManager.java

在这里插入图片描述

TransactionalCache.java

在这里插入图片描述

在这里插入图片描述

怎么关闭二级缓存

修改配置文件(mybatis-config.xml)

<setting name="cacheEnabled" value="false"/>

在这里插入图片描述

一级缓存(Spring整合Mybatis)

在未开启事务的情况下,每次查询Spring都会关闭旧的SqlSession而创建新的SqlSession,因此此时的一级缓存是没有生效的;
在开启事务的情况下,Spring模板使用threadLocal获取当前资源绑定的同一个SqlSession,因此此时一级缓存是有效的;

演示前准备

项目结构

在这里插入图片描述

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com</groupId>
    <artifactId>springboot-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <!-- web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

application.yml

server:
  port: 8080

spring:
  datasource:
    username: root
    password: admin
    url: jdbc:mysql://localhost:3306/db_mybatis?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      connection-timeout: 10000

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mapper.UserMapper">
    <select id="listUser" resultType="com.mybatis.entity.UserEntity">
        select * from tb_user
    </select>
</mapper>

AppMybatis.java

package com.mybatis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author honey
 * @date 2023-08-02 02:58:16
 */
@SpringBootApplication
public class AppMybatis {

    public static void main(String[] args) {
        SpringApplication.run(AppMybatis.class);
    }
}

UserEntity.java

package com.mybatis.entity;

import lombok.Data;

/**
 * @author honey
 * @date 2023-08-02 03:03:19
 */
@Data
public class UserEntity {

    private Long id;

    private String name;
}

UserMapper.java

package com.mybatis.mapper;

import com.mybatis.entity.UserEntity;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * @author honey
 * @date 2023-07-26 21:04:23
 */
@Mapper
public interface UserMapper {

    /**
     * 查询用户列表
     *
     * @return List<UserEntity>
     */
    List<UserEntity> listUser();
}

UserController.java

package com.mybatis.controller;

import com.mybatis.entity.UserEntity;
import com.mybatis.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author honey
 * @date 2023-08-02 03:09:13
 */
@RestController
@RequiredArgsConstructor
public class UserController {

    private final UserMapper userMapper;

    @RequestMapping("listUser")
    public void listUser(){
        List<UserEntity> list = userMapper.listUser();
        System.out.println(list);
    }
}

效果演示

不开启事务,调用多次接口

在这里插入图片描述

第一次调用

在这里插入图片描述

第二次调用

在这里插入图片描述

两次调用获取到的是不同的SqlSession,一级缓存不生效

开启事务,调用多次接口

在这里插入图片描述

第一次调用

在这里插入图片描述

第二次调用

在这里插入图片描述

两次调用获取到的也是不同的SqlSession,一级缓存不生效

不开启事务,接口中多次调用查询方法

在这里插入图片描述

第一次调用

在这里插入图片描述

第二次调用

在这里插入图片描述

两次调用获取到的依然是不同的SqlSession,一级缓存不生效

开启事务,接口中多次调用查询方法

在这里插入图片描述

第一次调用

在这里插入图片描述

第二次调用

在这里插入图片描述

两次调用获取到的是相同的SqlSession,一级缓存生效

总结

只有在同一个事务内执行查询,一级缓存才会生效。

源代码

MapperMethod.java
在这里插入图片描述

在Spring整合Mybatis的代码中,新增了SqlSessionTemplate类对DefaultSqlSession类的功能进行增强。

SqlSessionTemplate.java

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

SqlSessionUtils.java

在这里插入图片描述

在这里插入图片描述

能获取到SqlSessionHolder对象的前提是开启了事务。如果当前线程开启了事务,则不会直接关闭SqlSession对象,而是在下一次调用时复用SqlSession对象。

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

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

相关文章

【css】css调整文本间距

text-indent 属性用于指定文本第一行的缩进letter-spacing 属性用于指定文本中字符之间的间距line-height 属性用于指定行之间的间距&#xff1a;word-spacing 属性用于指定文本中单词之间的间距&#xff0c;这个和letter-spacing类似white-space 属性指定元素内部空白的处理方…

Java面向对象之方法的使用

文章目录 一、什么是方法二、方法的声明格式三、方法的分类四、方法的调用五、方法的注意点六、方法的重载七、可变形参的方法八、方法参数的值传递机制九、递归方法 一、什么是方法 方法(method)是类或对象行为特征的抽象&#xff0c;用来完成某个功能操作。在某些语言中也称…

JavaScript将CSV文件转为JSON

说在前面 相信平时大家或多或少都会有遇到过类似的情况&#xff1a; &#x1f64e;&#xff08;需求小姐姐&#xff09; &#x1f64e;‍♂&#xff08;我&#xff09; &#x1f64e;&#xff1a;这里我们需要做一个省市联动下拉框&#xff0c;你看看可以做吗&#xff1f; &am…

基于SSM 应急指挥平台-计算机毕设 附源码 13263

SSM应急指挥平台 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0…

DFS之连通性--迷宫

思路&#xff1a;就是通过dfs进行搜索&#xff0c;看是否能从给定的起点到给定的终点&#xff0c;当然也可以用bfs写&#xff0c;这里用到dfs写的 #include<bits/stdc.h> using namespace std; int n; char g[1010][1010]; int ax,ay,bx,by; int dx[4]{1,0,-1,0}; int dy…

pgSql 报错:timestamp without time zone >= character varying

在这里补充一点&#xff0c;数据库映射实体类不要使用LocalDateTime,驱动版本大概率不支持&#xff0c;出现奇奇怪怪的错&#xff1b; 在mysql 使用时&#xff0c;String 类型会隐式转成Date类型&#xff0c;使用mybatis-plus拼接就不会报错&#xff0c;换成pgSql就出现这个错…

模板方法模式:优化代码复用与扩展性的设计模式

模板方法模式&#xff1a;优化代码复用与扩展性的设计模式 什么是模板方法模式&#xff1f; 模板方法模式是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;将一些步骤的具体实现延迟到子类中。模板方法模式通过将算法的通用部分抽象出来&#xff0c;以模板…

华为OD机试真题 Java 实现【最小传输时延Ⅱ】【2023 B卷 200分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

计算一组数据的方差statistics.pvariance()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算一组数据的方差 statistics.pvariance() 选择题 下列说法错误的是? import statistics data [1, 2] print("【显示】data ", data) print(【执行】statistics.pvariance(data…

个人中心 - 实现修改用户头像、用户名或密码

目录 1. 修改用户头像 1.1 获取原来的用户头像和用户名 1.2 实现保存头像 2. 修改用户名或密码 1. 修改用户头像 本文是针对之前的一篇项目博客 - 博客系统 做的一个扩展功能. 1.1 获取原来的用户头像和用户名 想要修改头像, 那么就得先获取数据库中原来的头像, 此处顺便…

从零开始 Spring Cloud 8:Docker

从零开始 Spring Cloud 8&#xff1a;Docker 图源&#xff1a;laiketui.com Docker 可以帮助我们更方便地部署 Spring Cloud 应用。 环境准备 准备 Docker 环境可以参考 这篇文章。 操作镜像 docker 的镜像相关操作主要涉及以下命令&#xff1a; docker pull&#xff0c;…

什么是服务网格?

背景&#xff1a; 服务网格这个概念出来很久了&#xff0c;从 2017 年被提出来&#xff0c;到 2018 年正式爆发&#xff0c;很多云厂商和互联网企业都在纷纷向服务网格靠拢。像蚂蚁集团、美团、百度、网易等一线互联 网公司&#xff0c;都有服务网格的落地应用。服务网格是微服…

开源-基于ch9374b的KVM设计

文章目录 简介功能特性设计图实现功能开源链接 简介 平时总有一种需求&#xff0c;就是我在调试树莓派的时候&#xff0c;经常要在pc电脑和开发板之间来回操作&#xff0c;因此就需要两套键盘和鼠标&#xff0c;但是我的桌子实在是太小了&#xff0c;两套键鼠不能并排放置&…

浅谈Struts2请求解析过程

0x00前言 在使用Struts2的时候需要在web.xml中配置一个过滤器&#xff0c;来拦截用户发起的请求&#xff0c;并进行一些预处理&#xff0c;根据配置文件把请求分配给对应的action并将请求中的参数与action中的字段进行对应赋值。例如下面的例子&#xff0c;通过配置StrutsPrepa…

二叉树的前,中,后序的非递归实现(c++)

前言 对于二叉树来说&#xff0c;遍历它有多种方式&#xff0c;其中递归遍历是比较简单的&#xff0c;但是非递归的实现就有一定的难度&#xff0c;在这里介绍一种非递归实现二叉树遍历的方式。 1.前序遍历 1.1思路 其实对于二叉树的非递归实现&#xff0c;实际上就是用代码来…

Spring中Bean的实例化详细流程

还是举个例子&#xff0c;我有一个朋友小汪他远赴南方某城市打工。然后安定下来后他的朋友很想来家里玩&#xff0c;但是呢我这个朋友家里搞的很乱&#xff0c;所以他不好意思请朋友来家里玩。这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了&#xff0c;然后给他…

【LeetCode】一、链表反转

题目 题目&#xff1a;给定单链表头节点&#xff0c;将单链表的链接顺序反转过来 例&#xff1a; 输入&#xff1a;1->2->3->4->5 输出&#xff1a;5->4->3->2->1 要求&#xff1a;按照两种方式实现 解决办法 方式一&#xff1a;&#xff08;直接迭…

腾讯云服务器CVM镜像操作系统大全_win_linux

腾讯云CVM服务器的公共镜像是由腾讯云官方提供的镜像&#xff0c;公共镜像包含基础操作系统和腾讯云提供的初始化组件&#xff0c;公共镜像分为Windows和Linux两大类操作系统&#xff0c;如TencentOS Server、Windows Server、OpenCloudOS、CentOS Stream、CentOS、Ubuntu、Deb…

大一python编程题库和答案,大一python基础编程题库

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;大一python编程题库和答案山东理工大学&#xff0c;大一python编程题库和答案解析&#xff0c;今天让我们一起来看看吧&#xff01; 单项选择题 第一章python语法基础 1. Python 3.x 版本的保留字总数是C A 27 …

Python(五十七)列表生成式

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…