Spring Boot版本:2.3.4.RELEASE
本文的内容是Spring Boot中redis的使用,以及接口数据缓存的一种十分优雅的实现方式
顺带一提,我使用的Redis可视化工具是QuickRedis
使用redis前的准备工作
-
导入依赖
<!--redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--spring-boot-starter-cache--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> -
配置文件连接redis服务器
spring: redis: host: myserverhost port: 6379 password: 123456 jedis: pool: max-active: 8 # 连接池最大连接数,负值表示无限制 max-wait: -1 # 连接池最大阻塞等待时间,负值表示无限制 max-idle: 500 # 连接池中的最大空闲连接 min-idle: 0 # 连接池中的最小空闲连接 -
redis配置类
package com.cc.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.text.SimpleDateFormat; @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // 下面的配置是为了在redis可视化工具中可读 // ObjectMapper指定在转成json时的一些转换规则 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); // 把自定义objectMapper设置到jackson2JsonRedisSerializer中(可以不设置,使用默认规则) jsonRedisSerializer.setObjectMapper(objectMapper); // RedisTemplate默认的序列化方式使用的是JDK的序列化 // 设置key的序列化方式 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(jsonRedisSerializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new StringRedisSerializer()); return template; } }
目录
- RedisTemplate和StringRedisTemplate的区别和使用场景
- Redis的基本使用
- 优雅的处理接口数据缓存
RedisTemplate和StringRedisTemplate的区别和使用场景
两者的区别是他们使用的序列化类不同,
-
RedisTemplate使用的是JdkSerializationRedisSerializer
-
StringRedisTemplate使用的是StringRedisSerializer
RedisTemplate的默认序列化类JdkSerializationRedisSerializer在操作数据的时候,会将数据序列化成字节数组,如果缓存对象,需要对象实现Serializable接口。
这种序列化方式缓存的数据在Redis可视化工具上是不可读的。
场景:
当数据是对象类型,并且取出的时候不做任何转换,直接获取一个对象时,这种方式会比较好。
StringRedisTemplate的序列化类StringRedisSerializer只能存储字符串类型的value数据,当存储其他类型时必须转为String
这种序列化方式缓存的数据在Redis可视化工具上是可读的。
场景:
当数据本来就是字符串的时候,就可以用这个。
其他格式
在实际开发过程中,可以根据开发需要选择不同的序列化方式,如:jackson2JsonRedisSerializer、GenericJackson2JsonRedisSerializer等
Redis的基本使用
Redis提供了五大常用类型的操作方法:
- 字符串String:opsForValue()
- 列表List:opsForList()
- 集合Set:opsForSet()
- 哈希Hash:opsForHash()
- 有序集合ZSet:opsForZSet()
以下是redisTemplate的示例代码,具体可以在ApplicationTest中看到:
字符串String
@Test
public void stringTest() {
System.out.println("\n\nredis缓存字符串示例:");
redisTemplate.opsForValue().set("string", "字符串");
// 设置过期时间
redisTemplate.expire("string", 1, TimeUnit.MINUTES);
String string = redisTemplate.opsForValue().get("string").toString();
System.out.println("redisTemplate.opsForValue().get(\"string\"): " + string);
redisTemplate.delete("string");
}
列表List
@Test
public void listTest() {
System.out.println("\n\nredis缓存列表示例:");
// 添加元素到数组左侧
redisTemplate.opsForList().leftPush("list", "a");
redisTemplate.opsForList().leftPush("list", "b");
redisTemplate.opsForList().leftPush("list", "c");
// 添加元素到数组右侧
redisTemplate.opsForList().rightPush("list", "1");
redisTemplate.opsForList().rightPush("list", "2");
redisTemplate.opsForList().rightPush("list", "3");
// 批量添加元素到数组左侧/右侧
redisTemplate.opsForList().leftPushAll("list", "q", "w", "e", "r");
redisTemplate.opsForList().rightPushAll("list", "5", "6", "7", "8");
// 获取列表指定位置的值
System.out.println("redisTemplate.opsForList().index(\"list\", 0): " + redisTemplate.opsForList().index("list", 0));
System.out.println("redisTemplate.opsForList().index(\"list\", 100): " + redisTemplate.opsForList().index("list", 100));
// 获取指定区间的值
System.out.println("redisTemplate.opsForList().range(\"list\", 0, 3): " + redisTemplate.opsForList().range("list", 0, 3));
// 获取列表所有的值
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
redisTemplate.expire("list", 10, TimeUnit.SECONDS);
// 如果存在集合则添加元素
redisTemplate.delete("list");
redisTemplate.opsForList().leftPushIfPresent("list", "123");
redisTemplate.opsForList().rightPushIfPresent("list", "123");
// 此时应为空
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
redisTemplate.opsForList().rightPushAll("list", "a", "b", "c");
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
// 在第二个参数所在元素的左边插入第三个参数值
redisTemplate.opsForList().leftPush("list", "b", "bb");
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
// 在第二个参数所在元素的右边插入第三个参数值
redisTemplate.opsForList().rightPush("list", "a", "aa");
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
// 列表元素个数
System.out.println("redisTemplate.opsForList().size(\"list\"): " + redisTemplate.opsForList().size("list"));
// 移除元素左边/右边第一个元素
redisTemplate.opsForList().leftPop("list");
redisTemplate.opsForList().rightPop("list");
// 移除右边的元素,同时向左边加入一个元素
redisTemplate.opsForList().rightPopAndLeftPush("list", "z");
// 在指定位置插入元素,如果指定位置已有元素会覆盖,没有则新增,超过下标+n会报错
redisTemplate.opsForList().set("list", 1, "a");
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
/**
* 删除列表中指定的元素,
* 当l=0时,删除所有相同元素,
* 当l>0时,从左到右,删除第一个相同元素
* 当l<0时,从右到左,删除第一个相同元素
*/
int l = 0;
redisTemplate.delete("list");
redisTemplate.opsForList().rightPushAll("list", "a", "b", "c", "a", "b", "c");
redisTemplate.opsForList().remove("list", -1, "a");
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
// 截取元素长度,保留长度内的数据
redisTemplate.opsForList().trim("list", 0, 3);
System.out.println("redisTemplate.opsForList().range(\"list\", 0, -1): " + redisTemplate.opsForList().range("list", 0, -1));
}
集合Set
@Test
public void setTest() {
System.out.println("redis缓存set测试");
// 向集合内添加值
resetSet("set");
redisTemplate.opsForSet().add("set", "a", "b", "c", "c", "d", "d", "f");
// 获取集合中的值
Set set = redisTemplate.opsForSet().members("set");
System.out.println("set: " + set);
// 集合的长度
System.out.println("size: " + redisTemplate.opsForSet().size("set"));
// 随机获取集合中的元素
System.out.println("redisTemplate.opsForSet().randomMember(\"set\"): " + redisTemplate.opsForSet().randomMember("set"));
// 随机获取集合中指定数量的元素
System.out.println("redisTemplate.opsForSet().randomMembers(\"set\", 2): " + redisTemplate.opsForSet().randomMembers("set", 2));
// 检查指定的元素是否在集合中
System.out.println("redisTemplate.opsForSet().isMember(\"set\", \"b\"): " + redisTemplate.opsForSet().isMember("set", "b"));
// 转移变量的元素到目的变量
boolean move = redisTemplate.opsForSet().move("set", "b", "toSet");
if (move) {
System.out.println("redisTemplate.opsForSet().members(\"set\"): " + redisTemplate.opsForSet().members("set"));
System.out.println("redisTemplate.opsForSet().members(\"toSet\"): " + redisTemplate.opsForSet().members("toSet"));
}
// 弹出元素
redisTemplate.opsForSet().pop("set");
// 移除多个元素
Long removeCount = redisTemplate.opsForSet().remove("set", "a", "b");
System.out.println("removeCount: " + removeCount);
System.out.println("移除ab后的剩余元素:" + redisTemplate.opsForSet().members("set"));
{
// 比较两个set集合,并返回在第一个set中的差值
resetSampleSet("set");
resetSampleSetByValus("newSet", ",", "2", "3", "a", "b", "c");
System.out.println("redisTemplate.opsForSet().members(\"set\"): " + redisTemplate.opsForSet().members("set"));
System.out.println("redisTemplate.opsForSet().difference(\"set\", \"newSet\"): " + redisTemplate.opsForSet().difference("set", "newSet"));
}
{
// 比较两个set集合,将第一个set中的插值保存到指定set中
resetSampleSetByValus("set", "a", "b", "c");
resetSampleSetByValus("newSet", "1", "2", "3");
redisTemplate.opsForSet().differenceAndStore("set", "newSet", "saveSet");
System.out.println("redisTemplate.opsForSet().members(\"saveSet\"): " + redisTemplate.opsForSet().members("saveSet"));
}
{
// 随机获取不重复的指定数量的元素
resetSampleSetByValus("set", "1", "2", "3", "a", "b", "c");
System.out.println(redisTemplate.opsForSet().distinctRandomMembers("set", 2));
}
{
// 获取两个集合中的交集
resetSampleSetByValus("set", "a", "b", "c", "d", "e", "f", "g");
resetSampleSetByValus("newSet", "a", "b", "c", "1", "2", "3");
System.out.println(redisTemplate.opsForSet().intersect("set", "newSet"));
}
{
// 获取两个集合中的交集,并保存到指定集合上
resetSampleSetByValus("set", "a", "b", "c", "d", "e", "f", "g");
resetSampleSetByValus("newSet", "a", "b", "c", "1", "2", "3");
resetSet("saveSet");
redisTemplate.opsForSet().intersectAndStore("set", "newSet", "saveSet");
System.out.println("redisTemplate.opsForSet().members(\"saveSet\"): " + redisTemplate.opsForSet().members("saveSet"));
}
{
// 获取两个集合的合集
resetSampleSetByValus("set", "a", "b", "c", "d", "e", "f", "g");
resetSampleSetByValus("newSet", "a", "b", "c", "1", "2", "3");
System.out.println(redisTemplate.opsForSet().union("set", "newSet"));
}
{
// 获取两个集合的合集,并保存到指定集合上
resetSampleSetByValus("set", "a", "b", "c", "d", "e", "f", "g");
resetSampleSetByValus("newSet", "a", "b", "c", "1", "2", "3");
resetSet("saveSet");
redisTemplate.opsForSet().unionAndStore("set", "newSet", "saveSet");
System.out.println("redisTemplate.opsForSet().members(\"saveSet\"): " + redisTemplate.opsForSet().members("saveSet"));
}
}
// 重置set
private void resetSet(String key) {
redisTemplate.delete(key);
}
// 重置set,并且初始化值
private void resetSampleSet(String key) {
resetSampleSetByValus(key, "a", "b", "c", "d", "e", "f", "g");
}
// 重置set,并且初始化指定值
private void resetSampleSetByValus(String key, Object... values) {
redisTemplate.delete(key);
redisTemplate.opsForSet().add(key, values);
}
哈希Hash
@Test
public void hashTest() {
System.out.println("redis缓存hash测试");
// 添加元素
redisTemplate.opsForHash().put("hash", "key", "value");
Map<String, String> map = new HashMap<>();
map.put("name", "cc");
map.put("age", "10");
redisTemplate.opsForHash().putAll("hash", map);
// 查询元素
System.out.println("redisTemplate.opsForHash().get(\"hash\", \"key\"): " + redisTemplate.opsForHash().get("hash", "key"));
// 删除元素
redisTemplate.opsForHash().put("hash", "key1", "value1");
redisTemplate.opsForHash().put("hash", "key2", "value2");
redisTemplate.opsForHash().put("hash", "key3", "value3");
redisTemplate.opsForHash().delete("hash", "key1", "key2");
// 查询某key是否存在
System.out.println("redisTemplate.opsForHash().hasKey(\"hash\", \"key1\"): " + redisTemplate.opsForHash().hasKey("hash", "key1"));
// 批量查询,根据多个key查询value
System.out.println(redisTemplate.opsForHash().multiGet("hash", Arrays.asList("key1", "key2", "key3")));
// hash里的所有key和所有value
System.out.println("redisTemplate.opsForHash().keys(\"hash\"): " + redisTemplate.opsForHash().keys("hash"));
System.out.println("redisTemplate.opsForHash().values(\"hash\"): " + redisTemplate.opsForHash().values("hash"));
// 遍历所有键值对
Map<Object, Object> entries = redisTemplate.opsForHash().entries("hash");
for(Map.Entry<Object,Object> entry : entries.entrySet()){
System.out.println(entry.getKey() + ":" + entry.getValue());
}
// 自增,如果value是一个整型,redis提供了自增的函数
redisTemplate.opsForHash().increment("hash", "index", 1);
// hash表的大小
System.out.println("redisTemplate.opsForHash().size(\"hash\"): " + redisTemplate.opsForHash().size("hash"));
}
有序集合ZSet
@Test
public void zsetTest() {
System.out.println("redis缓存zset测试");
// 插入元素,并设置分数,元素将会按分数从小到大排序
redisTemplate.opsForZSet().add("zset", "a", 1);
// 插入多个元素
redisTemplate.opsForZSet().add("zset", "q", 1);
redisTemplate.opsForZSet().add("zset", "w", 2);
redisTemplate.opsForZSet().add("zset", "e", 3);
redisTemplate.opsForZSet().add("zset", "r", 4);
redisTemplate.opsForZSet().add("zset", "q", 4);
redisTemplate.opsForZSet().add("zset", "q", 5);
redisTemplate.opsForZSet().add("zset", "q", 6); // 插入相同value,会被覆盖分数
// 删除元素
redisTemplate.opsForZSet().remove("zset", "q");
// 增加元素的分数,并返回增加后的值
System.out.println("redisTemplate.opsForZSet().incrementScore(\"zset\", \"a\", 10): " + redisTemplate.opsForZSet().incrementScore("zset", "a", 10));
// 返回元素所在的排名
System.out.println("redisTemplate.opsForZSet().rank(\"zset\", \"a\"): " + redisTemplate.opsForZSet().rank("zset", "a"));
// 返回元素所在的排名,按分数从大到小排序
System.out.println("redisTemplate.opsForZSet().reverseRank(\"hash\", \"a\"): " + redisTemplate.opsForZSet().reverseRank("zset", "a"));
// 返回指定分数区间内的元素
System.out.println("redisTemplate.opsForZSet().rangeByScore(\"zset\", 1, 2): " + redisTemplate.opsForZSet().rangeByScore("zset", 1, 5));
// 返回指定分数区间内的元素,按分数从大到小排序
System.out.println("redisTemplate.opsForZSet().reverseRangeByScore(\"zset\", 1, 5): " + redisTemplate.opsForZSet().reverseRangeByScore("zset", 1, 5));
// 返回指定分数区间内,分数在最大值和最小值之间的元素
System.out.println("redisTemplate.opsForZSet().rangeByScore(\"zset\", 3, 4, 1, 5): " + redisTemplate.opsForZSet().rangeByScore("zset", 3, 4, 1, 5));
System.out.println("redisTemplate.opsForZSet().reverseRangeByScore(\"zset\", 3, 4, 1, 5): " + redisTemplate.opsForZSet().rangeByScore("zset", 3, 4, 1, 5));
// 根据分数区间获取集合中元素的数量
System.out.println("redisTemplate.opsForZSet().count(\"zset\", 1, 5): " + redisTemplate.opsForZSet().count("zset", 1, 5));
// 获取集合大小
System.out.println("redisTemplate.opsForZSet().size(\"zset\"): " + redisTemplate.opsForZSet().size("zset"));
// 获取指定元素的分数
System.out.println("redisTemplate.opsForZSet().score(\"zset\", \"a\"): " + redisTemplate.opsForZSet().score("zset", "a"));
// 移除指定索引范围处的元素
// redisTemplate.opsForZSet().removeRange("zset", 1, 2);
// 移除指定分数范围内的成员
// redisTemplate.opsForZSet().removeRangeByScore("zset", 1, 5);
redisTemplate.opsForZSet().add("newZSet", "a", 10);
redisTemplate.opsForZSet().add("newZSet", "b", 2);
redisTemplate.opsForZSet().add("newZSet", "c", 3);
redisTemplate.opsForZSet().add("newZSet", "d", 4);
// 获取两个集合的合集并保存到指定集合中,同一个元素的分数会相加
redisTemplate.opsForZSet().unionAndStore("zset", "newZSet", "unionZSet");
// 获取两个集合的交集并保存到指定集合中,同一个元素的分数会相加
redisTemplate.opsForZSet().intersectAndStore("zset", "newZSet", "intersectZSet");
// 遍历集合
Cursor<ZSetOperations.TypedTuple<Object>> scan = redisTemplate.opsForZSet().scan("zset", ScanOptions.NONE);
while (scan.hasNext()){
ZSetOperations.TypedTuple<Object> item = scan.next();
System.out.println(item.getValue() + ":" + item.getScore());
}
}
优雅的处理接口数据缓存
当我们熟悉了上面Redis的基本使用之后,要实现接口数据缓存相信不难,实现思路一般是这样:
-
响应接口请求
-
判断Redis中是否有缓存数据可以获取
-
是
从Redis中取出数据并返回
-
否
从数据库中获取,先存入Redis中再返回
-
-
请求结束
这种方式有一个问题,就是可能会在多个接口都写上第2步的逻辑代码,造成代码冗余,有一种更好的方式去实现,那就是Spring Cache。
首先我们需要添加依赖,其中hutool工具不是缓存功能必要的,但是可以辅助我们做到一些事情,后面会说明:
<!--spring-boot-starter-cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--Hutool Java工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.7</version>
</dependency>
然后在启动类中添加@EnableCaching注解,以开启缓存
package com.cc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class BusinessApplication {
public static void main(String[] args) {
SpringApplication.run(BusinessApplication.class, args);
}
}
重点来了,我们要写关于缓存的配置类:
RedisCacheConfig:
package com.cc.config;
import cn.hutool.json.JSONUtil;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* Spring Cache Redis,默认key的时候,@RequestBody的对象需要实现toString
* @author cc
* @date 2021-07-12 10:19
*/
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {
/**
* 过期时间配置,当需要设置其他缓存时间的时候,在这里添加,并且在本类的 getRedisCacheConfigurationMap 中更新
* @author cc
* @date 2021-07-12 10:19
*/
public interface CacheNames {
String LIST_DATA = "LIST_DATA"; // 列表数据
}
/**
* 过期时间设置
* @author cc
* @date 2021-07-12 10:24
*/
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
// 时间单位:秒
redisCacheConfigurationMap.put(CacheNames.LIST_DATA, this.getRedisCacheConfigurationWithTtl(300));
return redisCacheConfigurationMap;
}
/**
* 缓存key的生成策略
* 最终生成的key 为 cache类注解指定的cacheNames::类名:方法名#参数值1,参数值2...
* @author cc
* @date 2021-07-12 10:21
*/
@Override
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return target.getClass().getName() +
"-" +
method.getName() +
"#" +
JSONUtil.toJsonStr(params);
}
};
}
/**
* 配置缓存管理器
* @author cc
* @date 2021-07-12 10:20
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
// 默认缓存时间(秒)
this.getRedisCacheConfigurationWithTtl(60),
// 缓存过期时间设置
this.getRedisCacheConfigurationMap()
);
}
/**
* redis序列化
* @author cc
* @date 2021-07-12 10:20
*/
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
//关键点,spring cache 的注解使用的序列化都从这来,没有这个配置的话使用的jdk自己的序列化,实际上不影响使用,只是打印出来不适合人眼识别
return RedisCacheConfiguration.defaultCacheConfig()
// 将 key 序列化成字符串
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 将 value 序列化成 json
// Jackson2JsonRedisSerializer虽然效率和速度更快,但是不能反序列化泛型,所以再想到解决办法之前只能先用GenericJackson2JsonRedisSerializer了
// .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer(Object.class)))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
// 设置缓存过期时间,单位秒
.entryTtl(Duration.ofSeconds(seconds))
// 不缓存空值
.disableCachingNullValues();
}
}
接下来我们模拟两个接口:
- 获取分页数据
- 更新分页数据
package com.cc.controller;
import com.cc.config.RedisCacheConfig;
import com.cc.model.MyPageParam;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
public class TestController {
@Cacheable(value = RedisCacheConfig.CacheNames.LIST_DATA)
@PostMapping("/list")
public List<String> list(@RequestBody MyPageParam pageParam) {
return fakeData(pageParam);
}
@CacheEvict(value = RedisCacheConfig.CacheNames.LIST_DATA, allEntries=true)
@PostMapping("/update")
public String update() {
return "更新成功,缓存数据已清除";
}
// 模拟分页数据
private List<String> fakeData(MyPageParam pageParam) {
List<String> list = new ArrayList<>();
for (int i = 0; i < pageParam.getPageSize(); i++) {
list.add(pageParam.getPageNum().toString() + "-" + i);
}
return list;
}
@CacheEvict(value = "*", allEntries=true)
@PostMapping("/clear")
public String clear() {
return "删除所有缓存数据成功";
}
}
开始测试,重复调用/list接口获取数据:
{
"pageNum": 1,
"pageSize": 10
}
通过Redis可视化工具可以看到数据已经被成功缓存。
然后调用/update更新接口后再看,缓存的数据已经被删除了。
撒花
可以看到,在模拟的列表接口中,我们没有写一行Redis缓存的逻辑代码,仅需要在接口处使用@Cacheable注解开启缓存,自定义一个缓存Key,在配置类中默认缓存60秒,如果有定制的需要,就在配置类中增加。