Springboot(四十八)SpringBoot3开启缓存(本地缓存caffeine,远程缓存redis)

561 阅读7分钟

缓存是一种介于数据永久存储介质与应用程序之间的数据临时存储介质,使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能。

 

此外缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间。

 

缓存是个好东西啊,在项目中合理的使用缓存,能极大的加快我们的数据读取速度,以及尽可能的减轻数据库查询压力。

 

下边是我在Springboot中集成缓存的全过程。

 

一:开启缓存

1:添加pom依赖

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

 

2:开启缓存

在启动类上添加@EnableCaching注解,如下所示:

@SpringBootApplication
//开启缓存功能
@EnableCaching
public class SpringBootCacheApplication
{
public static void main(String[] args)
{
        SpringApplication.run(Springboot19CacheApplication.class, args);
    }
}

 

3:缓存相关注解

(1):@Cacheable

@Cacheable注解是缓存中没有值则放入值,缓存中有值则取值。

cacheNames/value用来指定缓存组件的名字
key缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)
keyGeneratorkey 的生成器。 key 和 keyGenerator 二选一使用
cacheManager可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
condition可以用来指定符合条件的情况下才缓存
unless否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)
sync是否使用异步模式。

示例代码:

@Cacheable(value = "data",key = "#s")
@GetMapping("/article/get")
public String getData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("查询到数据:"+ time);
    return " 查询到数据 :" + time;
}

 

(2):@CachePut

@CachePut 注解一般用于更新缓存数据,相当于缓存使用的是写模式中的双写模式。

cacheNames/value用来指定缓存组件的名字
key缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)
keyGeneratorkey 的生成器。 key 和 keyGenerator 二选一使用
cacheManager可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
condition可以用来指定符合条件的情况下才缓存
unless否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)
cacheResolver 指定自定义的缓存管理器,cacheManager缓存管理器与cacheResolver自定义解析器二选一使用

示例代码:

@CachePut(value = "data",key = "#s")
@GetMapping("/article/put")
public String putData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("存储数据:"+ time);
    return " 存储数据 :" + time;
}

 

(3):@CacheEvict

@CacheEvict 注解一般用于删除缓存数据,相当于缓存使用的是写模式中的失效模式。

cacheNames/value用来指定缓存组件的名字
key缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写)
keyGeneratorkey 的生成器。 key 和 keyGenerator 二选一使用
cacheManager可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。
condition可以用来指定符合条件的情况下才缓存
allEntries清除全部缓存
cacheResolver 指定自定义的缓存管理器,cacheManager缓存管理器与cacheResolver自定义解析器二选一使用
beforeInvocation- 1.默认是false,即在方法执行成功后触发删除缓存的操作
  • 2.如果方法抛出异常未能成功返回,不会触发删除缓存的操作
  • 3.当改为true时,方法执行之前会清除指定的缓存,这样不论方法执行成功还是失败都会清除缓存 |

示例代码:

@CacheEvict(value = "data",key = "#s")
@GetMapping("/article/rem")
public String remData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("清除数据 :"+time);
    return " 清除数据 :" + time;
}

 

我们分别访问一下三个接口:

http://localhost:8001/java/article/get?s=1111

浏览器输出:

查询到数据 :2023-11-21T15:03:00.511531

再次访问,浏览器中显示的数据是不变的,因此,缓存生效

 

http://localhost:8001/java/article/put?s=1111

浏览器输出:

存储数据 :2023-11-21T15:32:01.120100400

http://localhost:8001/java/article/put?s=1111

浏览器输出:

存储数据 :2023-11-21T15:32:01.120100400

 

http://localhost:8001/java/article/rem?s=1111

浏览器输出:

清除数据 :2023-11-21T15:33:39.106140400

这个时候缓存已经清除,自己可以多尝试几次,还挺有意思的。

 

二:引入caffeine缓存

Springboot自带的本地缓存性能一般般,这里我们进入caffeine缓存。 Caffeine 是一个现代化的 Java 缓存库,设计用于提供高性能和可伸缩性的本地缓存解决方案。适用于高并发以及快速访问数据的场景,因为内部实现了基于 ConcurrentHashMap 的数据结构,从而保证并发访问时的线程安全和高性能。

 

1:引入pom依赖

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

 

2:yml配置

spring:
  cache:
    type: caffeine
    cache-names: data
    caffeine:
      spec: initialCapacity=10,maximumSize=500,expireAfterWrite=30s # 这里设置了过期时间 30秒

 

3:代码测试

// ===============================================
// cache
@Cacheable(value = "data",key = "#s")
@GetMapping("/article/get")
public String getData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("查询到数据:"+ time);
    return " 查询到数据 :" + time;
}

@CachePut(value = "data",key = "#s")
@GetMapping("/article/put")
public String putData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("存储数据:"+ time);
    return " 存储数据 :" + time;
}

@CacheEvict(value = "data",key = "#s")
@GetMapping("/article/rem")
public String remData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("清除数据 :"+time);
    return " 清除数据 :" + time;
}

 

测试方式跟上边是一样的,这里不再赘述,试试就好。

 

三:引入redis缓存

Springboot也是可以集成redis缓存的,不要想多,就是将上边的caffeine换成redis。

 

Springboot集成redis请移步《Springboot(四十六)SpringBoot3整合redis并配置哨兵模式

 

1:yml配置

spring:
  cache:
    type: redis
    cache-names: data
    #cache-names: blog
    redis: # 过期时间
      time-to-live: 10m # 10m(10分钟)

 

2:测试代码:

// ======================================================
// cache
@Cacheable(value = "data:REDIS_SHOW_TEXT",key = "#s")
@GetMapping("/article/get")
public String getData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("查询到数据:"+ time);
    return " 查询到数据 :" + time;
}

@CachePut(value = "data:REDIS_SHOW_TEXT",key = "#s")
@GetMapping("/article/put")
public String putData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("存储数据:"+ time);
    return " 存储数据 :" + time;
}

@CacheEvict(value = "data:REDIS_SHOW_TEXT",key = "#s")
@GetMapping("/article/rem")
public String remData(String s)
{
    LocalDateTime now = LocalDateTime.now();
    String time = now.toString();
    System.out.println("清除数据 :"+time);
    return " 清除数据 :" + time;
}

 

测试方式跟上边是一样的,这里不再赘述,试试就好。

 

你可以在redis中查看一下缓存的key。

 

至此,Springboot就已经开启缓存了。

 

PS:redis缓存也可以使用java配置文件进行配置,代码如下所示:

package com.modules.cache.config;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;
import java.util.Random;

@Configuration
@EnableCaching
public class CacheConfig {

    private static final int MIN_EXPIRY_SECONDS = 3000// 最小过期时间(秒)
    private static final int MAX_EXPIRY_SECONDS = 6000// 最大过期时间(秒)

    /**
     * Redis 缓存配置 (这个玩意的优先级要高于yml配置文件,因此配置了这个玩意,不管你的yml配置的缓存是啥,redis一定会存一份)
     * 为了统一管理,我们还是在yml中配置
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory)
    {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        return RedisCacheManager.builder(redisCacheWriter)
                .cacheDefaults(redisCacheConfiguration())
                .build();
    }

    private RedisCacheConfiguration redisCacheConfiguration()
    {
        return RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()))
                .entryTtl(randomExpiry()); // 设置随机过期时间
    }

    /**
     * 随机缓存失效时间
     * @return
     */
    private Duration randomExpiry()
    {
        int expirySeconds = new Random().nextInt((MAX_EXPIRY_SECONDS - MIN_EXPIRY_SECONDS) + 1) + MIN_EXPIRY_SECONDS;
        return Duration.ofSeconds(expirySeconds);
    }
}

 

这里需要注意一下。这个玩意的优先级要高于yml配置文件,因此配置了这个玩意,不管你的yml配置的缓存是啥,redis一定会存一份。

 

为了统一管理,我们还是在yml中配置

 

上边的代码中我们尝试了两种缓存,分别是本地缓存caffeine和远程缓存redis。

 

有好的建议,请在下方输入你的评论。