Redis

分享一篇学习笔记,如有错误,请指出

配置

java
复制代码
@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); // 配置 ObjectMapper,支持 Java 8 时间类型 ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(mapper); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; }

Spring 是这样工作的:

  1. 读取配置: Spring Boot 启动时,RedisAutoConfiguration 类会读取 application.yml 中以 spring.data.redis(或旧版 spring.redis)开头的配置项。
  2. 创建工厂: Spring 自动根据这些配置创建一个 RedisConnectionFactory 的 Bean(默认是 Lettuce,也可以配置为 Jedis)。这个工厂里已经包含了 host、port、password 等连接信息。
  3. 注入参数: 当 Spring 扫描到你的 redisTemplate 方法时,发现它需要一个 RedisConnectionFactory 参数。Spring 就会把第 2 步自动创建好的那个工厂实例注入进来。
  4. 应用配置: 你的代码 template.setConnectionFactory(connectionFactory) 实际上就是把读取了 YML 配置的连接工厂塞给了 RedisTemplate。
yaml
复制代码
spring: data: redis: host: 127.0.0.1 # Redis 服务器地址 port: 6379 # Redis 端口 password: yourpassword # 如果有密码 database: 0 # 指定数据库索引 timeout: 3000ms # 连接超时时间 lettuce: # 如果使用的是默认的 Lettuce 客户端 pool: max-active: 8 # 连接池最大连接数 max-wait: -1ms # 连接池最大阻塞等待时间 max-idle: 8 # 连接池中的最大空闲连接 min-idle: 0 # 连接池中的最小空闲连接

Spring Boot 内部有一个类叫 RedisProperties。它使用了 @ConfigurationProperties(prefix = "spring.data.redis") 注解。

  1. YML 中的值被绑定到 RedisProperties 对象上。
  2. Spring Boot 的自动配置类(RedisAutoConfiguration)使用这个 RedisProperties 对象来构建 LettuceConnectionFactory
  3. 最后,这个 Factory 被传递给你的 @Bean 方法。

使用

首先引入

java
复制代码
@Resource private RedisTemplate<String, Object> redisTemplate;

使用过程最好搭配hutool工具类

java
复制代码
import cn.hutool.core.lang.TypeReference; import cn.hutool.json.JSONUtil;

首先常量类中要配置key

java
复制代码
public class RedisKeyConstants { /** * Redis 缓存键前缀 * 提示词 */ public static final String KEY = "key:"; }

使用,存缓存,一定要写过期时间!!!

java
复制代码
public VO getDetailById(Long id) { if (id == null) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "ID不能为空"); } // 缓存 key: scroll_prompt:detail:{id} String cacheKey = KEY + "detail:" + id; // 先查缓存 Object cached = redisTemplate.opsForValue().get(cacheKey); if (cached != null) { try { // Hutool 极简写法:先转为 JSONObject,再转为 Bean return JSONUtil.toBean(JSONUtil.parseObj(cached), VO.class); } catch (Exception e) { log.error("Hutool 反序列化失败,删除缓存", e); redisTemplate.delete(cacheKey); } } //查数据库 VO = ... // 存入缓存,一定要写过期时间 redisTemplate.opsForValue().set(cacheKey, VO, 30, TimeUnit.MINUTES); return VO; }

其他类似

注意的是有增删改时记得删除缓存

java
复制代码
// 1. 清除详情缓存 redisTemplate.delete(KEY + "detail:" + id);

还有就是分页查询

java
复制代码
public Page<VO> pageQuery(Request request) { // 1. 生成缓存 key String cacheKey = buildPageCacheKey(request); // 1. 查询缓存 Object cached = redisTemplate.opsForValue().get(cacheKey); if (cached != null) { try { // 1. 先把 Redis 返回的 LinkedHashMap 转为 Hutool 的 JSONObject // 2. 再通过 TypeReference 转回带泛型的 Page 对象 Page<VO> page = JSONUtil.toBean( JSONUtil.parseObj(cached), new TypeReference<Page<VO>>() {}, true // ignoreError: 是否忽略转换过程中的错误 ); return page; } catch (Exception e) { log.error("Hutool 反序列化失败", e); redisTemplate.delete(cacheKey); } } //查数据库 // 4. 存入缓存(过期时间建议 5-10 分钟) redisTemplate.opsForValue().set(cacheKey, voPage, 10, TimeUnit.MINUTES); return voPage; } //生成缓存key和删缓存可以封装成方法 /** * 生成分页查询的缓存 Key */ private String buildPageCacheKey(Request request) { StringBuilder keyBuilder = new StringBuilder(KEY); keyBuilder.append("page:"); // 添加查询条件 keyBuilder.append("req1:").append(StringUtils.isNotBlank(request.getPrompt()) ? request.getPrompt() : "null"); keyBuilder.append(":req2:").append(request.getStatus() != null ? request.getStatus() : "null"); keyBuilder.append(":current:").append(request.getCurrent()); keyBuilder.append(":size:").append(request.getPageSize()); return keyBuilder.toString(); } /** * 清除所有分页查询缓存 */ private void clearPageCache() { String pattern = KEY + "page:*"; Set<String> keys = redisTemplate.keys(pattern); if (keys != null && !keys.isEmpty()) { redisTemplate.delete(keys); } }

补充

使用过程中发现使用StringRedisTemplate更好

java
复制代码
@Resource private StringRedisTemplate stringRedisTemplate; public List<Case> getIndexCases() { String key = "index:cases"; // 1. 先查 Redis String jsonStr = stringRedisTemplate.opsForValue().get(key); // 2. 如果 Redis 有,转成对象列表返回 if (StrUtil.isNotBlank(jsonStr)) { // 使用 Hutool 将 JSON 字符串转为 List<Case> return JSONUtil.toList(jsonStr, Case.class); } // 3. 如果 Redis 没有,去查数据库 List<Case> caseList = caseMapper.selectList(null); // 4. 查完存入 Redis (设置过期时间,比如 30 分钟) // 使用 Hutool 将对象转为 JSON 字符串 stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(caseList), 30, TimeUnit.MINUTES); return caseList; }

工具类

java
复制代码
/** * Redis工具类 * 封装了 StringRedisTemplate + Hutool JSON,实现对象自动序列化 */ @Component @Slf4j public class RedisUtils { @Resource private StringRedisTemplate stringRedisTemplate; /** * 写入缓存 (默认不过期) * @param key 键 * @param value 值 (对象会自动转为 JSON 字符串) */ public void set(String key, Object value) { stringRedisTemplate.opsForValue().set(key, objToStr(value)); } /** * 写入缓存并设置过期时间 * @param key 键 * @param value 值 * @param time 过期时间 * @param timeUnit 时间单位 (例如 TimeUnit.MINUTES) */ public void set(String key, Object value, long time, TimeUnit timeUnit) { stringRedisTemplate.opsForValue().set(key, objToStr(value), time, timeUnit); } /** * 读取缓存 (返回单个对象) * @param key 键 * @param clazz 目标对象的 Class * @return 对象,如果 key 不存在则返回 null */ public <T> T get(String key, Class<T> clazz) { String json = stringRedisTemplate.opsForValue().get(key); if (StrUtil.isBlank(json)) { return null; } return JSONUtil.toBean(json, clazz); } /** * 读取缓存 (返回 List 集合) * @param key 键 * @param clazz 集合中元素的 Class * @return List 对象 */ public <T> List<T> getList(String key, Class<T> clazz) { String json = stringRedisTemplate.opsForValue().get(key); if (StrUtil.isBlank(json)) { return null; } return JSONUtil.toList(json, clazz); } /** * 读取缓存 (返回原生 String) */ public String get(String key) { return stringRedisTemplate.opsForValue().get(key); } /** * 删除缓存 */ public void delete(String key) { stringRedisTemplate.delete(key); } /** * 检查 Key 是否存在 */ public Boolean hasKey(String key) { return stringRedisTemplate.hasKey(key); } // --- 私有辅助方法 --- private String objToStr(Object value) { if (value == null) { return null; } if (value instanceof String) { return (String) value; } return JSONUtil.toJsonStr(value); } }

实战

java
复制代码
@Resource private RedisUtils redisUtils; // 直接存,不用管opsForValue redisUtils.set(key, code, 5, TimeUnit.MINUTES); // 取的时候 String cacheCode = redisUtils.get(key);
java
复制代码
@Resource private RedisUtils redisUtils; public List<Case> getIndexCases() { String key = "index:cases"; // 1. 尝试从缓存获取 List // 泛型自动处理:告诉它 key 是什么,List 里装的是什么类 (Case.class) List<Case> cacheList = redisUtils.getList(key, Case.class); // 2. 如果缓存有,直接返回 if (cacheList != null && !cacheList.isEmpty()) { return cacheList; } // 3. 缓存没有,查数据库 List<Case> dbList = caseMapper.selectList(null); // 4. 写入缓存 (设置30分钟过期) // 这里直接把 List 传进去,工具类会自动转成 JSON 字符串存入 if (dbList != null) { redisUtils.set(key, dbList, 30, TimeUnit.MINUTES); } return dbList; }
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP