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

SpringBoot系列——使用Spring Cache和Redis实现查询数据缓存

文章目录

    • 1. 前言
    • 2. 缓存
      • 2.1 什么是缓存
      • 2.2 使用缓存的好处
      • 2.3 缓存的成本
      • 2.4 Spring Cache和Redis的优点
    • 3. Spring Cache基础知识
      • 3.1 Spring Cache的核心概念
      • 3.2 Spring Cache的注解
        • 3.2.1 SpEL表达式
        • 3.2.2 @Cacheable
        • 3.2.3 @CachePut
        • 3.2.4 @CacheEvict
    • 4. 实现查询数据缓存
      • 4.1 准备工作
      • 4.2 添加依赖
      • 4.3 修改配置文件
      • 4.4 配置缓存管理器
      • 4.5 使用Spring Cache注解
      • 4.6 测试
        • 4.6.1 查询测试
        • 4.6.2 更新、删除测试
    • 5. 总结

1. 前言

在现代应用程序中,查询缓存的使用已经变得越来越普遍。它不仅能够显著提高系统的性能,还能提升用户体验。缓存通过在内存中存储频繁访问的数据,减少对数据库或其他存储系统的访问,从而加快数据读取速度。在这篇文章中,我们将探讨缓存的基本概念、重要性以及如何使用Spring Cache和Redis实现查询数据缓存 。

2. 缓存

2.1 什么是缓存

缓存是一种临时存储机制,用于在内存中保存频繁访问的数据。它可以是硬件(如CPU缓存)或软件(如应用程序缓存)。缓存的主要目的是通过减少数据访问的延迟,提高系统的响应速度。以下是缓存的一些关键特性:

  • 临时性:缓存中的数据通常是临时的,会在一段时间后失效或被替换。
  • 快速访问:由于缓存数据存储在内存中,访问速度非常快。
  • 空间有限:缓存的存储空间通常有限,因此需要有效的管理策略,如LRU(最近最少使用)策略。

2.2 使用缓存的好处

  1. 提高性能:缓存可以显著减少数据读取的时间,因为内存访问速度比硬盘或网络存储快很多。
  2. 减轻数据库负载:缓存可以减少数据库的查询次数,从而减轻数据库的负载,提升整体系统的稳定性和可扩展性。
  3. 节省资源:通过减少对后端系统的访问,缓存可以帮助节省带宽和计算资源。
  4. 提高用户体验:快速的数据访问可以显著提升用户体验,特别是在需要频繁读取数据的应用场景中。

2.3 缓存的成本

  1. 内存消耗:缓存需要占用系统的内存资源,过多的缓存可能会影响其他应用程序的性能。
  2. 数据一致性:缓存中的数据可能会与数据库中的数据不一致,尤其是在数据频繁更新的场景中。需要设计有效的缓存失效策略来保证数据的一致性。
  3. 复杂性增加:引入缓存机制会增加系统的复杂性,需要处理缓存的管理、更新和失效等问题。
  4. 维护成本:缓存系统需要定期监控和维护,以确保其高效运行。

2.4 Spring Cache和Redis的优点

为了实现高效的数据缓存,Spring Boot提供了Spring Cache模块,而Redis则是一个强大的缓存数据库。结合使用Spring Cache和Redis,能够充分发挥二者的优点,实现高效的数据缓存。

  • Spring Cache的优点
    • 简化缓存操作:Spring Cache提供了一系列注解(如@Cacheable@CachePut@CacheEvict),简化了缓存的使用,使开发者能够专注于业务逻辑。
    • 灵活的缓存管理:Spring Cache支持多种缓存提供者(如EhCache、Hazelcast、Redis等),可以根据具体需求选择合适的缓存实现。
    • 透明的缓存机制:Spring Cache使得缓存操作对业务代码透明,开发者无需关心缓存的具体实现细节。
  • Redis的优点
    • 高性能:由于数据存储在内存中,Redis的读写速度非常快,能够处理每秒数百万级别的请求。
    • 丰富的数据结构:Redis支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,能够满足不同场景下的数据存储需求。
    • 持久化支持:虽然Redis主要用于内存存储,但它也提供了数据持久化的功能,可以将数据定期保存到磁盘,防止数据丢失。
    • 分布式支持:Redis支持主从复制、哨兵模式和集群模式,能够实现高可用性和数据的水平扩展。
    • 灵活的过期策略:Redis支持为每个键设置过期时间,自动删除过期数据,方便实现缓存失效策略。

3. Spring Cache基础知识

在Spring Boot中,Spring Cache提供了一套简洁且强大的缓存抽象机制,帮助开发者轻松地将缓存集成到应用程序中。以下是Spring Cache的一些核心概念和常用注解。

3.1 Spring Cache的核心概念

  1. CacheManager

    • 定义CacheManager是Spring Cache的核心接口,负责管理多个缓存实例。它是缓存操作的入口点,提供了获取和操作缓存实例的方法。
    • 实现:Spring提供了多种CacheManager实现,如ConcurrentMapCacheManagerEhCacheCacheManagerRedisCacheManager等。不同的实现适用于不同的缓存存储机制。
  2. Cache

    • 定义Cache是缓存的具体实现,负责存储和检索缓存数据。它提供了基本的缓存操作,如putgetevict等。
    • 实现:具体的Cache实现依赖于底层的缓存存储机制,如内存缓存、Redis缓存等。

3.2 Spring Cache的注解

3.2.1 SpEL表达式

因为Spring Cache使用SpEL表达式来动态生成缓存键,所以在学习Spring Cache的注解之前我们还要先简单了解一下SpEL表达式的语法,这部分可以先不看懂,在后面看注解的时候回来看即可。

SpEL表达式的语法类似于Java的表达式语法,支持以下几种操作:

  1. 字面量
    • 数字:1, 2.5
    • 字符串:'hello', "world"
    • 布尔值:true, false
    • 空值:null
  2. 属性和方法
    • 访问对象的属性:#user.name
    • 调用对象的方法:#user.getName()
  3. 运算符
    • 算术运算:+, -, *, /, %
    • 比较运算:==, !=, <, >, <=, >=
    • 逻辑运算:&&, ||, !
  4. 集合和数组
    • 访问集合元素:#users[0]
    • 集合操作:#users.size(), #users.isEmpty()
  5. 条件运算符
    • 三元运算符:condition ? trueValue : falseValue
    • Elvis运算符:expression ?: defaultValue
  6. 变量
    • 定义和使用变量:#variableName

接下来进入Spring Cache注解的学习:

3.2.2 @Cacheable
  • 作用@Cacheable注解用于标注需要缓存的方法。当该方法被调用时,Spring Cache会先检查缓存中是否存在对应的数据。如果存在,则直接返回缓存数据;如果不存在,则执行方法并将结果存入缓存。
  • 示例
    @RestController("/users")
    @RequiredArgsConstructor
    public class UserController {private final UserService userService;@Cacheable(value = "user", key = "#id")public User getUser(Long id) {// 获取用户的逻辑return userService.findById(id);}
    }
    
  • 参数
    • value:指定缓存的名称。
    • key:指定缓存的键,可以使用SpEL表达式。
3.2.3 @CachePut
  • 作用@CachePut注解用于标注需要更新缓存的方法。即使缓存中已经存在数据,该方法仍然会执行,并将结果更新到缓存中。
  • 示例
    @RestController("/users")
    @RequiredArgsConstructor
    public class UserController {private final UserService userService;@CachePut(value = "user", key = "#user.id")public User updateUser(User user) {// 更新用户的逻辑return userService.save(user);}
    }
    
  • 参数
    • value:指定缓存的名称。
    • key:指定缓存的键,可以使用SpEL表达式。
3.2.4 @CacheEvict
  • 作用@CacheEvict注解用于标注需要清除缓存的方法。当该方法被调用时,Spring Cache会清除对应的缓存数据。
  • 示例
    @RestController("/users")
    @RequiredArgsConstructor
    public class UserController {private final UserService userService;@CacheEvict(value = "user", key = "#id")public void deleteUser(Long id) {// 删除用户的逻辑userService.deleteById(id);}
    }
    
  • 参数
    • value:指定缓存的名称。
    • key:指定缓存的键,可以使用SpEL表达式。
    • allEntries:如果设置为true,则清除缓存中的所有数据。

4. 实现查询数据缓存

4.1 准备工作

  1. Redis安装与配置:

这里可以自行查找文章进行安装和配置,网上优质文章很多👻。

  1. 创建Product实体类:
@Data
@AllArgsConstructor
public class Product implements Serializable {private Long id;private String name;private Integer category;private String description;private Integer stock;}
  1. 创建枚举类ResultEnum
@Getter
public enum ResultEnum {/* 成功状态码 */SUCCESS(1, "操作成功!"),/* 错误状态码 */FAIL(0, "操作失败!"),/* 参数错误:10001-19999 */PARAM_IS_INVALID(10001, "参数无效"),PARAM_IS_BLANK(10002, "参数为空"),PARAM_TYPE_BIND_ERROR(10003, "参数格式错误"),PARAM_NOT_COMPLETE(10004, "参数缺失"),/* 用户错误:20001-29999*/USER_NOT_LOGGED_IN(20001, "用户未登录,请先登录"),USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),USER_NOT_EXIST(20004, "用户不存在"),USER_HAS_EXISTED(20005, "用户已存在"),/* 系统错误:40001-49999 */FILE_MAX_SIZE_OVERFLOW(40003, "上传尺寸过大"),FILE_ACCEPT_NOT_SUPPORT(40004, "上传文件格式不支持"),/* 数据错误:50001-599999 */RESULT_DATA_NONE(50001, "数据未找到"),DATA_IS_WRONG(50002, "数据有误"),DATA_ALREADY_EXISTED(50003, "数据已存在"),AUTH_CODE_ERROR(50004, "验证码错误"),/* 权限错误:70001-79999 */PERMISSION_UNAUTHENTICATED(70001, "此操作需要登陆系统!"),PERMISSION_UNAUTHORIZED(70002, "权限不足,无权操作!"),PERMISSION_EXPIRE(70003, "登录状态过期!"),PERMISSION_TOKEN_EXPIRED(70004, "token已过期"),PERMISSION_LIMIT(70005, "访问次数受限制"),PERMISSION_TOKEN_INVALID(70006, "无效token"),PERMISSION_SIGNATURE_ERROR(70007, "签名失败");// 状态码int code;// 提示信息String message;ResultEnum(int code, String message) {this.code = code;this.message = message;}public int code() {return code;}public String message() {return message;}public void setCode(int code) {this.code = code;}public void setMessage(String message) {this.message = message;}
}
  1. 创建统一返回结果封装类Result

相关文章可以看这里:Spring Boot3统一结果封装

@Data
@NoArgsConstructor
public class Result<T> implements Serializable {// 操作代码Integer code;// 提示信息String message;// 结果数据T data;public Result(ResultEnum resultCode) {this.code = resultCode.code();this.message = resultCode.message();}public Result(ResultEnum resultCode, T data) {this.code = resultCode.code();this.message = resultCode.message();this.data = data;}public Result(String message) {this.message = message;}//成功返回封装-无数据public static Result<String> success() {return new Result<String>(ResultEnum.SUCCESS);}//成功返回封装-带数据public static <T> Result<T> success(T data) {return new Result<T>(ResultEnum.SUCCESS, data);}//失败返回封装-使用默认提示信息public static Result<String> error() {return new Result<String>(ResultEnum.FAIL);}//失败返回封装-使用返回结果枚举提示信息public static Result<String> error(ResultEnum resultCode) {return new Result<String>(resultCode);}//失败返回封装-使用自定义提示信息public static Result<String> error(String message) {return new Result<String>(message);}
}

4.2 添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

4.3 修改配置文件

spring:data:redis:# Redis服务器地址host: ${shijun.redis.host}# Redis服务器端口port: ${shijun.redis.port}# Redis服务器认证密码password: ${shijun.redis.password}# Redis数据库索引database: ${shijun.redis.database}

4.4 配置缓存管理器

/*** 配置类,用于设置缓存管理器及相关配置,以启用缓存功能** @author shijun* @date 2024/06/13*/
@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {/*** 配置Redis键的序列化方式** @return StringRedisSerializer,用于序列化和反序列化Redis中的键*/private RedisSerializer<String> keySerializer() {return new StringRedisSerializer();}/*** 配置Redis值的序列化方式** @return GenericJackson2JsonRedisSerializer,使用Jackson库以JSON格式序列化和反序列化Redis中的值*/private RedisSerializer<Object> valueSerializer() {return new GenericJackson2JsonRedisSerializer();}/*** 缓存前缀,用于区分不同的缓存命名空间,一般以模块名或者服务名命名,这里暂时写cache*/public static final String CACHE_PREFIX = "cache:";/*** 配置缓存管理器,使用Redis作为缓存后端** @param redisConnectionFactory Redis连接工厂,用于创建Redis连接* @return RedisCacheManager,Redis缓存管理器实例*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {// 配置序列化,解决乱码的问题,设置缓存名称的前缀和缓存条目的默认过期时间RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()// 设置键的序列化器.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))// 设置值的序列化器.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))// 设置缓存名称的前缀.computePrefixWith(name -> CACHE_PREFIX + name + ":")// 设置缓存条目的默认过期时间为300秒.entryTtl(Duration.ofSeconds(300));// 创建非锁定的Redis缓存写入器RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisConnectionFactory));// 返回Redis缓存管理器实例,使用上述配置return new RedisCacheManager(redisCacheWriter, config);}}

分析:
StringRedisSerializer :使用 StringRedisSerializer 将缓存的键序列化为字符串。因为Redis中的键通常是字符串类型,使用字符串序列化器可以确保键在Redis中以可读的形式存储,便于调试和管理。

GenericJackson2JsonRedisSerializer :使用 GenericJackson2JsonRedisSerializer 将缓存的值序列化为JSON格式,可读性高并且便于人工排查数据。

4.5 使用Spring Cache注解

由于我们的缓存的数据源来自于数据库,而数据库的数据是会发生变化的,因此,如果当数据库中数据发生变化,而缓存却没有同步,此时就会有数据一致性问题存在,在一些并发场景会出现问题。

这里采用Cache Aside Pattern 即旁路缓存模式:缓存调用者在更新完数据库后再去更新缓存,也称之为双写方案。

  • 读流程:
    image-20240613224855076

分析:

  1. 应用程序首先从缓存中查找数据。
  2. 如果缓存命中,则直接返回缓存中的数据。
  3. 如果缓存未命中,则从数据库中读取数据,并将读取到的数据写入缓存,以便
  4. 后续请求可以直接从缓存中获取。
  • 写流程
    image-20240613221100300

分析:

  1. 应用程序首先更新数据库中的数据。
  2. 然后使缓存中的对应数据失效
@Slf4j
@RestController("/products")
public class ProductController {/*** 根据ID获取产品信息* 通过@Cacheable注解,当请求的产品ID在缓存中存在时,直接从缓存中获取产品信息,减少数据库查询** @param id 产品ID* @return 返回查询结果,包含指定ID的产品信息*/@GetMapping("/getProductById")@Cacheable(value = "productsCache", key = "#id")public Result<Product> getProductById(Long id) {// 当从数据库获取数据时会打印,如果是从缓存中查询并不会执行到这里。log.info("从数据库获取产品: id = {}", id);Product product = new Product(id, "product", 100, "课本", 10);return Result.success(product);}/*** 更新产品信息* 通过@CacheEvict注解,当更新产品时,清除缓存中对应产品的数据,确保获取到最新的数据* 设置allEntries为true,表示清除整个缓存中的所有产品数据** @param product 产品对象,包含更新后的详细信息* @return 返回更新结果,成功更新时返回成功标志*/@PutMapping("/updateProduct")@CacheEvict(value = "productsCache", key = "#product.id")public Result updateProduct(@RequestBody Product product) {// 更新操作return Result.success();}/*** 删除指定ID的产品* 通过@CacheEvict注解,当删除产品时,清除缓存中对应产品的数据** @param id 待删除产品的ID* @return 返回删除结果,成功删除时返回成功标志*/@DeleteMapping("/deleteProductById")@CacheEvict(value = "productsCache", key = "#id")public Result deleteProductById(Long id) {// 删除操作return Result.success();}}

4.6 测试

4.6.1 查询测试

因为我们在查询接口上使用的@Cacheable接口,所以当执行查询操作时,第一次查询会从数据库中获取,因此会输出「从数据库获取产品: id = x」,此时查看Redis控制台会发现出现一个对应的缓存,之后的每次查询都会从Redis中查询(控制台不会输出「从数据库获取产品: id = x」),直到对应的缓存数据时间结束。

  1. 发送查询请求:
image-20240613203320506
  1. 查看Redis中的缓存数据:

通过观察可以发现CacheConfig类中的序列化配置起作用了,Redis中的数据不再是一堆乱码,并且在右上角还有我们之前配置的缓存的过期时间(我们之前配置的300s)。

image-20240613203847598
  1. 查看控制台发现本次查询为从数据库查询:
image-20240613212356495
  1. 再次发送会发现数据成功的查询了:
image-20240613203438434
  1. 再次查询控制台发现并没有输出从数据库获取产品: id = 1,说明本次查询为从Redis缓存中获取数据。
4.6.2 更新、删除测试

因为之前我们在更新和删除接口上使用的@CacheEvict注解,所以当执行更新或者删除操作时,会将Redis中对应的产品缓存数据删除。

  1. 分别发送更新请求和删除请求,然后再次查看Redis中的缓存数据:
image-20240613215742944 image-20240613221930757
  1. 可以发现Redis当中对应的缓存数据被删除了,符合我们的设计:
image-20240613204329734

5. 总结

在本文中,我们详细介绍了如何在Spring Boot项目中使用Spring Cache和Redis实现数据缓存,并简单讲解了使用Cache Aside Pattern来解决数据一致性问题,希望对大家学习有所帮助。如有问题,大家可以私信或者在评论区询问😊。

相关文章:

SpringBoot系列——使用Spring Cache和Redis实现查询数据缓存

文章目录 1. 前言2. 缓存2.1 什么是缓存2.2 使用缓存的好处2.3 缓存的成本2.4 Spring Cache和Redis的优点 3. Spring Cache基础知识3.1 Spring Cache的核心概念3.2 Spring Cache的注解3.2.1 SpEL表达式3.2.2 Cacheable3.2.3 CachePut3.2.4 CacheEvict 4. 实现查询数据缓存4.1 准…...

【算法】(C语言):冒泡排序、选择排序、插入排序

冒泡排序 从第一个数据开始到第n-1个数据&#xff0c;依次和后面一个数据两两比较&#xff0c;数值小的在前。最终&#xff0c;最后一个数据&#xff08;第n个数据&#xff09;为最大值。从第一个数据开始到第n-2个数据&#xff0c;依次和后面一个数据两两比较&#xff0c;数值…...

iOS项目怎样进行二进制重排

什么是二进制重排 &#xff1f; 在iOS项目中&#xff0c;二进制重排&#xff08;Binary Reordering 或者 Binary Rearrangement&#xff09;是一种优化技术&#xff0c;主要目的是通过重新组织应用程序的二进制文件中的代码和数据段&#xff0c;来提高应用程序的性能&#xff…...

CentOS中使用SSH远程登录

CentOS中使用SSH远程登录 准备工作SSH概述SSH服务的安装与启动建立SSH连接SSH配置文件修改SSH默认端口SSH文件传输 准备工作 两台安装CentOS系统的虚拟机 客户机&#xff08;192.168.239.128&#xff09; 服务器&#xff08;192.168.239.129&#xff09; SSH概述 Secure S…...

spring @Autowire注解作用

终于有人把Autowired注解讲清楚了&#xff0c;赞&#xff01;&#xff01;&#xff01;_autowired-CSDN博客...

密码学原理精解【5】

这里写目录标题 移位密码概述代码 希尔密码( Z 256 Z_{256} Z256​)待加密长度被3整除待加密长度不一定被3整除加解密文件 移位密码 概述 以 z 26 运算为例 , k 为密钥 加密&#xff1a; e k ( x ) ( x k ) m o d 26 解密&#xff1a; d k ( x ) ( x − k ) m o d 26 以z_{…...

Unity3D 资源管理YooAsset原理分析与详解

引言 Unity3D 是一款广泛应用于游戏开发、虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;等领域的强大游戏开发引擎。在开发过程中&#xff0c;资源管理是一项至关重要的任务&#xff0c;它直接影响到游戏的性能和用户体验。YooAsset 是一个基于 Un…...

npm install puppeteer 报错 npm ERR! PUPPETEER_DOWNLOAD_HOST is deprecated解决办法

npm install puppeteer 报错如下&#xff1a; npm ERR! PUPPETEER_DOWNLOAD_HOST is deprecated. Use PUPPETEER_DOWNLOAD_BASE_URL instead. npm ERR! Error: ERROR: Failed to set up Chrome v126.0.6478.126! Set "PUPPETEER_SKIP_DOWNLOAD" env variable to sk…...

浙大版PTA《Python 程序设计》题目集 参考答案

浙大版PTA《Python 程序设计》题目集 参考答案 本答案配套详解教程专栏&#xff0c;欢迎订阅&#xff1a; PTA浙大版《Python 程序设计》题目集 详解教程_少侠PSY的博客-CSDN博客 01第1章-1 从键盘输入两个数&#xff0c;求它们的和并输出 aint(input()) # 输入a的值 bint(…...

“拆分盘投资:机遇与风险并存

一、引言 随着互联网技术的日新月异&#xff0c;金融投资领域迎来了前所未有的变革&#xff0c;其中拆分盘作为一种新兴的投资模式&#xff0c;正逐渐进入公众的视野。其独特的价值增长逻辑和创新的投资机制&#xff0c;为投资者开辟了新的财富增值渠道。本文旨在深入探讨拆分…...

Java面试题系列 - 第2天

题目&#xff1a;Java中的线程池模型及其配置策略 背景说明&#xff1a;在Java多线程编程中&#xff0c;线程池是一种高效的线程复用机制&#xff0c;能够有效管理和控制线程的创建与销毁&#xff0c;避免频繁创建和销毁线程带来的性能开销。理解和掌握线程池的配置策略对于优…...

AGI|Transformer自注意力机制超全扫盲攻略,建议收藏!

一、前言 2017年&#xff0c;谷歌团队推出一篇神经网络的论文&#xff0c;首次提出将“自注意力”机制引入深度学习中&#xff0c;这一机制可以根据输入数据各部分重要性的不同而分配不同的权重。当ChatGPT震惊世人时&#xff0c;Transformer也随之进入大众视野。一夜之间&…...

QT+OpenCV在Android上实现人脸实时检测与目标检测

一、功能介绍 在当今的移动应用领域&#xff0c;随着技术的飞速发展和智能设备的普及&#xff0c;将先进的计算机视觉技术集成到移动平台&#xff0c;特别是Android系统中&#xff0c;已成为提升用户体验、拓展应用功能的关键。其中&#xff0c;目标检测与人脸识别作为计算机视…...

常见网络攻击方式及防御方法

1. DDOS攻击&#xff08;分布式拒绝服务攻击&#xff09; 概念&#xff1a;借助于C/S&#xff08;客户端/服务器&#xff09;技术&#xff0c;将多个计算机联合起来作为攻击平台&#xff0c;对一个或多个目标发动DDOS攻击&#xff0c;从而成倍地提高拒绝服务攻击的威力。防护方…...

使用 ESP32 实现无线对讲机功能涉及音频采集、音频传输以及音频播放等多个方面。实现无线对讲机功能的基本步骤和示例代码。

硬件准备 两个 ESP32 开发板两个 MAX9814 麦克风模块&#xff08;或其他兼容的模拟麦克风模块&#xff09;两个 MAX98357A DAC 模块&#xff08;或其他兼容的音频放大器模块&#xff09;扬声器 接线 麦克风模块 -> ESP32 ADC 引脚ESP32 DAC 引脚 -> 音频放大器模块 -&…...

SpringBoot项目,配置文件pom.xml的结构解析

pom.xml 是 Maven 项目对象模型&#xff08;Project Object Model&#xff09;的配置文件&#xff0c;它定义了 Maven 项目的基本设置和构建过程。以下是 pom.xml 文件的基本结构和一些常见元素的解析&#xff1a; 项目声明 (<project>): <modelVersion>: 通常设置…...

教程:Spring Boot中集成Memcached的详细步骤

教程&#xff1a;Spring Boot中集成Memcached的详细步骤 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在现代应用开发中&#xff0c;缓存是提升性能和扩展性…...

Websocket通信实战项目(图片互传应用)+PyQt界面+python异步编程(async) (上)服务器端python实现

Rqtz : 个人主页 ​​ 共享IT之美&#xff0c;共创机器未来 ​ Sharing the Beauty of IT and Creating the Future of Machines Together 目录 项目背景 ​编辑​专有名词介绍 服务器GUI展示 功能(位置见上图序号) 客户端GUI展示&#xff08;h5cssjs&#xf…...

实验一 MATLAB \ Python数字图像处理初步

一、实验目的&#xff1a; 1&#xff0e;熟悉及掌握在MATLAB\Python中能够处理哪些格式图像。 2&#xff0e;熟练掌握在MATLAB\Python中如何读取图像。 3&#xff0e;掌握如何利用MATLAB\Python来获取图像的大小、颜色、高度、宽度等等相关信息。 4&#xff0e;掌握如何在M…...

echarts柱状选中shadow阴影背景宽度设置

使用line&#xff0c;宽度增大到所需要的宽度&#xff0c;设置下颜色透明度就行 tooltip: {trigger: axis,//把阴影的层级往下降z:-15,axisPointer: {type: line,lineStyle: {color: rgba(150,150,150,0.3),width: 44,type: solid,},}, }, series: [{type: bar,barWidth:20,//…...

ArrayBuffer 对象常见的几个用途

ArrayBuffer 在 JavaScript 中的用途广泛&#xff0c;主要用于处理二进制数据。 ArrayBuffer 对象、 TypedArray 视图和 DataView 视图是 JavaScript 操作二进制数据的一个接口。本文介绍ArrayBuffer 对象的常见的一些用法。 1. 网络传输二进制数据 使用方法&#xff1a;通过 …...

STC89C52RC单片机设计的FM收音机+自动搜台+存储电台(程序+原理图+PCB)

资料下载地址&#xff1a;STC89C52RC单片机设计的FM收音机自动搜台存储电台&#xff08;程序原理图PCB) 1、实物图 2、部分程序 #include <reg52.h> #include "tea5767.h" #include "delay.h" #include "lcd1602.h" //K1:上一台 K2:下一…...

【若依】关闭当前标签页并跳转路由到其他页面

使用场景如&#xff1a;当在新增/编辑路由页面提交成功后&#xff0c;需要关闭当前页&#xff0c;并跳转回列表页。 实现代码&#xff1a; this.$store.dispatch("tagsView/delView", this.$route); //关闭当前页 this.$router.replace({ path: "/xxx/xxx"…...

防爆智能手机如何解决危险环境下通信难题?

在化工厂、石油行业、矿山等危险环境中&#xff0c;通信安全一直是难题。传统手机因不具备防爆功能&#xff0c;可能引发火花、爆炸等安全风险&#xff0c;让工作人员在关键时刻难以及时沟通。但如今&#xff0c;防爆智能手机的出现彻底改变了这一现状&#xff01; 安全通信&am…...

软件测试最全面试题及答案整理(2024最新版)

1、你的测试职业发展是什么? 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自己&#xff0c;不断…...

11 - matlab m_map地学绘图工具基础函数 - 绘制航迹、椭圆、风向玫瑰图和特定的圆形区域的有关函数及其用法

11 - matlab m_map地学绘图工具基础函数 - 绘制航迹、椭圆、风向玫瑰图和特定的圆形区域的有关函数及其用法 0. 引言1. 关于m_track2. 关于m_range_ring3. 关于m_ellipse4. 关于m_windrose5. 结语 0. 引言 本篇介绍下m_map中绘制航迹图函数&#xff08;m_track&#xff09;、绘…...

长安链安装及使用问题

1. 关于golang编译出错: Get “https://proxy.golang.org/chainmaker.org/chainmaker/common/v2/v/v2.2.0.mod“: dial 在网上查阅资料后发现是自己的golang版本太低(1.3一下),因为goalng在最初开发时,国内基本上都会遇到依赖下载不了的问题&#xff0c; 然而在1.3版本后,go…...

大学生竞赛管理系统-计算机毕业设计源码37276

大学生竞赛管理系统的设计与实现 摘 要 随着教育信息化的不断发展&#xff0c;大学生竞赛已成为高校教育的重要组成部分。传统的竞赛组织和管理方式存在着诸多问题&#xff0c;如信息不透明、效率低下、管理不便等。为了解决这些问题&#xff0c;提高竞赛组织和管理效率&#x…...

去中心化 RAG 先行者,KIP Protocol 如何保护数据所有权、激活 AI 资产

AI 时代&#xff0c;人人都应实现 KnowledgeFi 的梦想或许并不遥远&#xff0c;KIP Protocol 正在生动践行这一价值理念&#xff0c;带动去中心化数字产权的创建与盈利&#xff0c;面向 CryptoAI 的蓝海市场迈出创新探索的技术步伐&#xff0c;朝着 Web3 行业打造去中心化 AI 的…...

numpy库(python)

文章目录 1.numpy简介2.安装numpy3.ndarry : numpy库的心脏3.1 创建数组3.2数据类型3.3dtype NumPy是用Python.进行科学计算&#xff0c;尤其是数据分析时&#xff0c;所用到的一个基础库。它是大量Python 数学和科学计算包的基础&#xff0c;比如后面要讲到的pandas)库就用到了…...