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

封装一个redis获取并解析数据的工具类

redis获取并解析数据工具类

  • 实现代码
  • 使用示例

实现代码

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;@Slf4j
@Component
public class RedissonDataUtils {private static RedissonClient redissonClient;@Autowiredpublic void setRedissonClient(RedissonClient client) {RedissonDataUtils.redissonClient = client;}/*** 校验 RedissonClient 是否已注入*/private static boolean clientReady() {if (redissonClient == null) {log.error("[RedissonDataUtils] RedissonClient 未注入,无法执行Redis操作");return false;}return true;}/*** 从 Redis 获取数据,如果不存在或解析失败则返回默认值** @param key          Redis 键* @param type         目标数据类型* @param defaultValue 默认值* @param <T>          数据类型* @return 解析后的数据对象或默认值*/public static <T> T getOrDefault(String key, Class<T> type, T defaultValue) {return get(key, type, null).orElse(defaultValue);}public static <T> T getOrDefault(String key, Class<T> type, T defaultValue, Codec codec) {return get(key, type, codec).orElse(defaultValue);}/*** 从 Redis 获取数据,如果不存在或解析失败则通过 Supplier 获取默认值** @param key             Redis 键* @param type            目标数据类型* @param defaultSupplier 默认值提供者(延迟执行)* @param <T>             数据类型* @return 解析后的数据对象或默认值*/public static <T> T getOrDefaultSupplier(String key, Class<T> type, Supplier<T> defaultSupplier) {return get(key, type, null).orElseGet(defaultSupplier);}public static <T> T getOrDefaultSupplier(String key, Class<T> type, Supplier<T> defaultSupplier, Codec codec) {return get(key, type, codec).orElseGet(defaultSupplier);}/*** 从 Redis 获取对象并进行安全解析* 1. 首选类型直返(与缓存编解码一致时)* 2. 若为字符串则尝试 JSON 反序列化* 3. 任何异常均捕获并记录,返回 Optional.empty()** @param key  缓存键* @param type 目标类型* @param <T>  泛型* @return Optional<T> 安全返回*/public static <T> Optional<T> get(String key, Class<T> type, Codec codec) {if (StrUtil.isBlank(key) || ObjectUtil.isNull(type)) {return Optional.empty();}if (!clientReady()) {return Optional.empty();}try {RBucket<Object> bucket;if (codec != null) {bucket = redissonClient.getBucket(key, codec);} else {bucket = redissonClient.getBucket(key);}Object cacheVal = bucket.get();if (cacheVal == null) {return Optional.empty();}T parsed = tryParse(cacheVal, type);return Optional.ofNullable(parsed);} catch (Exception e) {log.error("[RedissonDataUtils] 获取并解析缓存异常, key={}, type={}, error:", key, type.getSimpleName(), e);return Optional.empty();}}public static <T> List<T> getList(String key, Class<T> elementType) {return getList(key, elementType, null);}/*** 从 Redis 获取列表并进行安全解析** @param key         缓存键* @param elementType 列表元素类型* @param <T>         泛型* @return 不为 null,若无数据返回空列表*/public static <T> List<T> getList(String key, Class<T> elementType, Codec codec) {if (StrUtil.isBlank(key) || ObjectUtil.isNull(elementType)) {return Collections.emptyList();}if (!clientReady()) {return Collections.emptyList();}try {RBucket<Object> bucket;if (codec != null) {bucket = redissonClient.getBucket(key, codec);} else {bucket = redissonClient.getBucket(key);}Object cacheVal = bucket.get();if (cacheVal == null) {return Collections.emptyList();}// 优先处理已反序列化的列表if (cacheVal instanceof List) {try {@SuppressWarnings("unchecked")List<T> list = (List<T>) cacheVal;return CollUtil.isEmpty(list) ? Collections.emptyList() : list;} catch (ClassCastException ex) {// 继续尝试 JSON 解析}}// 处理字符串 JSONif (cacheVal instanceof String) {String json = (String) cacheVal;if (StrUtil.isBlank(json)) {return Collections.emptyList();}List<T> list = JSON.parseObject(json, new TypeReference<List<T>>() {});return CollUtil.isEmpty(list) ? Collections.emptyList() : list;}// 兜底:将对象转字符串再尝试解析String json = JSON.toJSONString(cacheVal);List<T> list = JSON.parseObject(json, new TypeReference<List<T>>() {});return CollUtil.isEmpty(list) ? Collections.emptyList() : list;} catch (Exception e) {log.error("[RedissonDataUtils] 获取并解析列表缓存异常, key={}, elementType={}", key, elementType.getSimpleName(), e);return Collections.emptyList();}}/*** 获取缓存,若不存在则通过回源函数加载并写入缓存(带可选 TTL)* 注意:此方法未加分布式锁,若有并发回源风险,请在业务层配合 Redisson 锁控制** @param key        缓存键* @param type       目标类型* @param loader     回源加载器,返回空时不写缓存* @param ttlSeconds 过期秒数,<=0 表示不过期* @param <T>        泛型* @return 数据实例或 null* @example redissonDataUtils.getOrLoad("scrm:xx", CustomerDTO.class, () -> service.findById(0L).map(CustomerMapper::toDTO).orElse(null), 1800L);*/public static <T> T getOrLoad(String key, Class<T> type, Supplier<T> loader, long ttlSeconds, Codec codec) {// 先取缓存Optional<T> cached = get(key, type, codec);if (cached.isPresent()) {return cached.get();}if (!clientReady()) {// 客户端未准备好时,直接回源,但不写缓存try {return loader == null ? null : loader.get();} catch (Exception e) {log.error("[RedissonDataUtils] 回源加载异常(客户端未就绪), key={}, type={}", key, type == null ? "null" : type.getSimpleName(), e);return null;}}// 回源加载T loaded;try {loaded = loader == null ? null : loader.get();} catch (Exception e) {log.error("[RedissonDataUtils] 回源加载异常, key={}, type={}", key, type == null ? "null" : type.getSimpleName(), e);return null;}if (loaded == null) {return null;}// 写缓存(尽量容错,不影响主流程)try {set(key, loaded, ttlSeconds, codec);} catch (Exception e) {log.warn("[RedissonDataUtils] 写入缓存失败(忽略), key={}", key, e);}return loaded;}/*** 写入缓存(可设置过期时间)** @param key        缓存键* @param value      缓存值* @param ttlSeconds 过期秒数,<=0 表示不过期*/public static void set(String key, Object value, long ttlSeconds, Codec codec) {if (StrUtil.isBlank(key)) {return;}if (!clientReady()) {return;}try {RBucket<Object> bucket;if (codec != null) {bucket = redissonClient.getBucket(key, codec);} else {bucket = redissonClient.getBucket(key);}if (ttlSeconds > 0) {bucket.set(value, ttlSeconds, TimeUnit.SECONDS);} else {bucket.set(value);}} catch (Exception e) {log.error("[RedissonDataUtils] 写入缓存异常, key={}", key, e);}}/*** 尝试将缓存值解析为目标类型*/private static <T> T tryParse(Object cacheVal, Class<T> type) {if (cacheVal == null) {return null;}// 1) 已经是目标类型if (type.isInstance(cacheVal)) {return type.cast(cacheVal);}// 2) 字符串 JSONif (cacheVal instanceof String) {String json = (String) cacheVal;if (StrUtil.isBlank(json)) {return null;}return JSON.parseObject(json, type);}// 3) 兜底:转 JSON 再解析String json = JSON.toJSONString(cacheVal);return JSON.parseObject(json, type);}
}

使用示例

  • 获取对象,未获取到使用默认值
// 1. 使用直接默认值
InfoVo info = RedissonDataUtils.getOrDefault("employee:1001", InfoVo.class, new InfoVo("默认员工", "默认部门")
);// 2. 使用 Supplier 默认值(延迟创建)
InfoVo Info = RedissonDataUtils.getOrDefault("employee:1001", InfoVo.class, () -> {InfoVo defaultVo = new InfoVo();defaultVo.setName("系统生成员工");defaultVo.setDepartment("技术部");defaultVo.setCreateTime(new Date());return defaultVo;}
);// 3. 基本类型示例
Integer userScore = RedissonDataUtils.getOrDefault("user:score:1001", Integer.class, 0
);// 4. 字符串示例
String userName = RedissonDataUtils.getOrDefault("user:name:1001", String.class, "匿名用户"
);
  • 获取对象
Optional<InfoVo> empOpt = RedissonDataUtils.get(rKey, InfoVo.class);
  • 获取列表
List<InfoVo> list = RedissonDataUtils.getList(rKey, InfoVo.class);
  • 缓存未命中则回源并写缓存(TTL 30 分钟)
InfoVo vo = RedissonDataUtils.getOrLoad(rKey,InfoVo.class,() -> ucService.loadVo(req.getInfoId()),1800L
);
  • 写缓存
RedissonDataUtils.set(rKey, infoVo, 1800L);
http://www.xdnf.cn/news/20008.html

相关文章:

  • FPGA学习笔记——SDR SDRAM简介
  • 【golang长途旅行第37站】Redis连接池
  • OCR 发票识别与验真接口:助力电子化发票新时代
  • 融云:当我们谈论 AI 重构业务时,我们到底在谈论什么
  • 【Android】SharedPreferences轻量级持久化存储
  • 【题解】洛谷P1776 宝物筛选 [单调队列优化多重背包]
  • C++----模板特化以及模板声明与定义分离问题
  • AT32网线拔插下,modbus tcp断线重连
  • Linux awk命令完全指南:从原理到实战,搞定文本处理难题
  • 【AI】人工智能 传统和现代 架构和算法的演变历史
  • windows安装谷歌浏览器地址
  • TypeScript `infer` 关键字详解(从概念到实战)
  • AGV 搬运小车路径规划:从地图构建到路径决策的技术全解析
  • 打通 Flutter 与原生状态管理:Android ViewModel 的运用
  • SpringBoot+PDF.js实现按需分片加载(包含可运行样例源码)
  • C++小游戏
  • 腾讯开源HunyuanWorld-Voyager突破性原生3D重建与视频扩散框架
  • 计算机大数据毕业设计选题:基于Spark+hadoop的全球香水市场趋势分析系统
  • 优思学院|5个为什么(5 Whys)分析法一文讲清楚
  • AI编写自动点击器 - 毫秒级精准鼠标连点器
  • kafka:【1】概念关系梳理
  • kvm 虚拟机如何安装 qemu-guest-agent
  • kali_linux
  • 【Linux】线程封装
  • 【FastDDS】Layer DDS之Domain ( 04-DomainParticipantFactory)
  • 采用基于模型的方法实现车辆SOA威胁分析自动化
  • wpf 自定义密码文本框,并且可以双向绑定
  • 吱吱企业通讯软件以安全为核心,构建高效沟通与协作一体化平台
  • 什么是Agent?小白如何学习使用Agent?一篇文档带你详细了解神秘的Agent
  • 容器tomcat镜像制作