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

Spring Boot缓存

文章目录

  • Spring Boot缓存
    • 1. SpringBoot开启MyBatis缓存+ehcache
    • 2. Spring Boot原生缓存基于Redis 的Cacheable 注解使用

Spring Boot缓存

1. SpringBoot开启MyBatis缓存+ehcache

引入依赖

 <!--添加缓存-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.7.13</version>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

添加缓存的配置文件 ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--指定一个文件目录,当EhCache把数据写到硬盘上时,将把数据写到这个文件目录下
             user.home :         用户主目录
             user.dir :          用户当前工作目录
             java.io.tmpdir :    默认临时文件路径
         -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache"/>

    <!--
    name:                            缓存名称
    eternal:                         true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false
    timeToIdleSeconds:               设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态
    timeToLiveSeconds:               设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义
    maxElementsInMemory:             内存中最大缓存对象数;maxElementsInMemory界限后,会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行
    memoryStoreEvictionPolicy:       当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)
    maxElementsOnDisk:               硬盘中最大缓存对象数,若是0表示无穷大
    overflowToDisk:                  是否保存到磁盘,当系统宕机时
    diskPersistent:                  是否缓存虚拟机重启期数据,是否持久化磁盘缓存,当这个属性的值为true时,系统在初始化时会在磁盘中查找文件名为cache名称,后缀名为index的文件,这个文件中存放了已经持久化在磁盘中的cache的index,找到后会把cache加载到内存,要想把cache真正持久化到磁盘,写程序时注意执行net.sf.ehcache.Cache.put(Element element)后要调用flush()方法
    diskSpoolBufferSizeMB:           这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
    diskExpiryThreadIntervalSeconds: 磁盘失效线程运行时间间隔,默认为120秒
    clearOnFlush:                    内存数量最大时是否清除
    -->
    <!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则默认缓存策略-->


    <defaultCache eternal="false" maxElementsInMemory="1000"
                  overflowToDisk="true" diskPersistent="true"
                  timeToIdleSeconds="0" timeToLiveSeconds="600"
                  memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="myCache"
            eternal="false"
            maxElementsInMemory="200"
            overflowToDisk="false"
            diskPersistent="true"
            timeToIdleSeconds="0"
            timeToLiveSeconds="300"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="myCache1"
            eternal="false"
            maxElementsInMemory="200"
            overflowToDisk="false"
            diskPersistent="true"
            timeToIdleSeconds="0"
            timeToLiveSeconds="10"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>

设置项目启动时使用缓存

@EnableCaching
public class GoodsApplication {
    public static void main(String[] args) {
        SpringApplication.run(GoodsApplication.class, args);
    }

}

在application.yml配置中读取ehcache.xml文件

spring:
  cache:
    ehcache:
      config: classpath:ehcache.xml

Spring Cache注解使用

@Cacheable使用缓存

可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。

@Cacheable可以指定三个属性,value、key和condition

@Cacheable("myCache")
    public MessageJson<Carousel> findList(){
        //log.info("日志信息:查询轮播图列表");
        List<Carousel> carousels = carouselService.list();
        return carousels!=null?MessageJson.success(carousels):MessageJson.error();
    }

@CacheEvict 清除缓存

可以指定的属性有value、key、condition、allEntries、beforeInvocation

 @CacheEvict(value = "myCache")
    public MessageJson addOrder(Long id,@RequestHeader Integer token, Long count){}

2. Spring Boot原生缓存基于Redis 的Cacheable 注解使用

引入redis依赖

<!-- Spring Boot Starter Data Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/shop?allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 1234
  redis:
    password: 123456
    database: 1
    sentinel:
      master: mymaster # 主节点名称
      nodes:
        - localhost:26379
        - localhost:26380
        - localhost:26381
    timeout: 5000ms # 连接超时时间
  cache:
    ehcache:
      config: classpath:ehcache.xml

编写Redis配置类

Redis 的默认序列化器是 JdkSerializationRedisSerializer,但是在实际使用中,由于其序列化后的大小通常比较大,因此我们通常使用StringRedisSerializer 或者 Jackson2JsonRedisSerializer 将缓存值序列化为字符串或者 JSON 格式。

/**
 *@ClassName ResdisConfig
 *@Description  Redis配置类
 *@Author Administrator
 *@Date 2024/12/17 16:47
 */
@Configuration
public class RedisConfig {
    /**
     * 哨兵模式
     * @return RedisConnectionFactory
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        // 创建RedisSentinelConfiguration对象
        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
                .master("mymaster") // 设置主节点名称,必须与Sentinel配置中的名称一致
                .sentinel("localhost", 26379)
                .sentinel("localhost", 26380)
                .sentinel("localhost", 26381);

        // 如果有密码,请在这里设置
        sentinelConfig.setPassword(RedisPassword.of("123456"));

        return new LettuceConnectionFactory(sentinelConfig);
    }

    /**
     * redisTemplate 默认使用JDK的序列化机制, 存储二进制字节码, 所以自定义序列化类
     * @param redisConnectionFactory redis连接工厂类
     * @return RedisTemplate
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory
                                                               redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
                Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL,
                JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
       // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

Cacheable 注解

@Cacheable("myCache")//开启缓存
public MessageJson<Carousel> findList(){
    //log.info("日志信息:查询轮播图列表");
    List<Carousel> carousels = carouselService.list();
    return carousels!=null?MessageJson.success(carousels):MessageJson.error();
}

带参数的

    @GetMapping("/getProById")
    @Cacheable(value = "proById",key = "#id")
    public MessageJson<Product> getProById(Long id){
        Product product = productService.getById(id);
        return product!=null?MessageJson.success(product):MessageJson.error();
    }

缓存的清除@CacheEvict

@CacheEvict(value = "orders", key = "#id")
public MessageJson addOrder(Long id,@RequestHeader Integer token, Long count){
    Product product = productService.getById(id);

    Orders orders = new Orders();
    orders.setUserId(token);
    orders.setProductId(id);
    orders.setCount(count);
    orders.setTotalAmount(product.getPrice()*count);

    product.setStock(product.getStock()-count);
    product.setSales(product.getSales()+count);
    productService.updateById(product);
    return orderService.save(orders)?MessageJson.success():MessageJson.error();
}

yml配置

#连接数据库
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/shop?allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 1234
  cache:
    ehcache:
      type: redis
      redis:
        cache-null-values: false # 缓存null值
        time-to-live: 3600ms # 缓存时间
        use-key-prefix: true # 缓存key前缀
      cache-names: productCache # 缓存名称
  redis:
    host: 127.0.0.1
    password: 123456
    database: 1
    port: 6379

缓存管理

通过#分隔,后面部分表示此 Cache 的TTL(单位:秒)

@GetMapping("/getProById")
@Cacheable(value = "proById#3600",key = "#id")
public MessageJson<Product> getProById(Long id){
    Product product = productService.getById(id);
    return product!=null?MessageJson.success(product):MessageJson.error();
}

RedisConfig内序列化

    /**
     * redisTemplate 默认使用JDK的序列化机制, 存储二进制字节码, 所以自定义序列化类
     * @param redisConnectionFactory redis连接工厂类
     * @return RedisTemplate
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory
                                                               redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
                Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL,
                JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
       // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

编写 RedisCacheManager

/**
 * @Author: wzy
 * @Date: 2024/12/23 16:26
 * @Description: redis缓存配置类
 */
public class MyRedisCacheManager extends RedisCacheManager {

    public MyRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }

    public RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        String arrays[] = StringUtils.delimitedListToStringArray(name,"#");  //proCache#1000
        if(arrays.length>1){
            name = arrays[0];
            cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(Long.parseLong(arrays[1])));
        }

        return super.createRedisCache(name,cacheConfig);
    }
}

使用我自定义的 MyRedisCacheManager 配置CacheConfig如下

/**
 * @Author: wzy
 * @Date: 2024/12/23 16:05
 * @Description:
 */
@Configuration
@EnableCaching //开启缓存
public class CacheConfig extends CachingConfigurerSupport {

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.database}")
    private int database;

    @Bean
    public CacheManager cacheManager() {
        //默认配置
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60)) //默认时间
                .computePrefixWith(cacheName -> {
                    return "myCache:" + cacheName;
                });

        MyRedisCacheManager myRedisCacheManager =
                new MyRedisCacheManager(
                        RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory())
                        ,defaultCacheConfig);
        return myRedisCacheManager;
    }


    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        //创建连接Redis工厂
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        redisStandaloneConfiguration.setDatabase(database);
        redisStandaloneConfiguration.setPassword(password);

        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration);
        return factory;
    }
}

http://www.kler.cn/a/461024.html

相关文章:

  • 在Typora中实现自动编号
  • PostgreSQL的备份方式
  • C++ 设计模式:备忘录模式(Memento Pattern)
  • android studio android sdk下载地址
  • Mac 安装Mysql启动Mysql以及数据库的常规操作
  • 1Panel自建RustDesk服务器方案实现Windows远程macOS
  • 【MySQL】第一弹----库的操作及数据类型
  • 网络安全问题解答
  • 尚硅谷Vue3入门到实战 —— 02 编写 App 组件
  • axios拦截器底层实现原理
  • 基于SpringBoot+Vue的旅游推荐系统
  • [pdf、epub]260道《软件方法》强化自测题业务建模需求分析共216页(202412更新)
  • Doris安装部署
  • 实现单例模式的五种方式
  • jQuery学习笔记1
  • 无人机任务载荷系统之电子对抗技术!
  • 使用PyTorch实现的二分类模型示例,综合了CNN、LSTM和Attention技术
  • MyBatis-Plus 中的分页插件配置
  • 在C++中,dynamic_cast是一种用于在类的继承体系中进行安全向下转型
  • 搭建ZooKeeper分布式集群
  • 2、单片机、CC2530、zigbee期末考试选择、填空题含答案
  • 如何确保Kafka集群的高可用?
  • Cursor小试1.生成一个网页的接口请求工具
  • Django 管理界面实现自动提交和动态字段选项
  • 鸿蒙HarmonyOS应用开发 探索 HarmonyOS Next-从开发到实战掌握 HarmonyOS Next 的分布式能力
  • C++中宏的使用方法