FEATURED · 精选文章

黑马点评之缓存工具封装

发布时间 / 2026/9/2 6:15:31
来源 / 创域科博编辑部
栏目 / 资讯中心
黑马点评之缓存工具封装 1.1缓存工具的定位和选择方法1和方法3配对起来解决一些常见的缓存问题方法2和方法4配对起来是为了解决热点key的。1.2设置逻辑过期时间方法和设置TTL过期时间public class CacheClient { private StringRedisTemplate stringRedisTemplate; public CacheClient(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate stringRedisTemplate; } public void set(String key, Object value, Long time, TimeUnit unit){ stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit); } public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){ //设置逻辑过期,把value封装到redisData对象里面去存就能诞生逻辑过期时间了 RedisData redisData new RedisData(); redisData.setData(value); redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time))); //plusSeconds是加上时间的unit是单位time是传进来的时间参数但是不能确定是秒所以需要用toSeconds去转一下 //写入Redis stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData),time,unit); }1.2.1设置TTL过期时间public void set(String key, Object value, Long time, TimeUnit unit){stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit);}因为stringRedisTemplate要求的类型是string而传value的类型是Object所以需要把对象序列化为json字符串。1.2.2设置逻辑过期时间方法public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){//设置逻辑过期,把value封装到redisData对象里面去存就能诞生逻辑过期时间了RedisData redisData new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));//plusSeconds是加上时间的unit是单位time是传进来的时间参数但是不能确定是秒所以需要用toSeconds去转一下//写入RedisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData),time,unit);}1.3用缓存空值解决缓存击穿public R,ID R queryWithPassThrough( String keyPrefix, ID id, ClassR type, FunctionID,R dbFallback, Long time, TimeUnit unit){ String key CACHE_SHOP_KEY id;//把这个转为字符串方便使用前面标明cache shop 方便区分id //1.从redis查缓存 String shopJson stringRedisTemplate.opsForValue().get(key);//以店铺id做为key,这样就得到店铺的json字符串 //2.判断是否存在 if (StrUtil.isNotBlank(shopJson)) { //3.存在直接返回 return JSONUtil.toBean(shopJson, type);//R的类型是type所以反序列化这边放的是type } //判断命中的是否是空值 if(shopJson !null){ //返回一个错误信息 return null; } //4.不存在根据id查询数据库 R r dbFallback.apply(id);//因为范型这边的查数据库语句不好写不像idname这种数据库都有的字段所以要定义一个函数从外面传 //5.不存在返回错误 if (rnull) { //将空值写入redis stringRedisTemplate.opsForValue().set(key,,CACHE_NULL_TTL,TimeUnit.MINUTES); //返回错误信息 return null; } //6.存在写入redis this.set(key,r, time, unit); //这个时间注意应该是传的时间而不是直接写死的 //因为用的是opsForValue所以是value是string类型的所以后面的shop还得转为string //7.返回 return r; }因为是封装的缓存工具所以不确定返回值类型是什么所以要用泛型用R来代表返回值 ClassR type这个是泛型的推荐方便将来告诉具体是什么类型R的类型就是type。id的类型不一定是long类型的不能写死所以id的类型也不确定所以也要用泛型。 R r dbFallback.apply(id);因为范型这边的查数据库语句不好写不像idname这种数据库都有的字段所以要定义一个函数从外面传所以这里的代码是一个数据库查询的逻辑。因为是个有参有返回的函数所以用的是FunctiondbFallback是数据库的后备逻辑在这段代码里面的意思是先查redisredis查失败了才走它所以是后备逻辑。Shop shop cacheClient.queryWithPassThrough (CACHE_SHOP_KEY,id,Shop.class, this::getById,CACHE_SHOP_TTL,TimeUnit.MINUTES);1.4利用逻辑过期时间解决缓存击穿private static final ExecutorService CACHE_REBUILD_EXECUTOR Executors.newFixedThreadPool(10); //创建一个线程池 public R,ID R queryWithLogicalExpire( String keyPrefix, ID id, ClassR type , FunctionID ,R dbFallback,Long time, TimeUnit unit){ String key keyPrefix id;//把这个转为字符串方便使用前面标明cache shop 方便区分id //1.从redis查缓存 String json stringRedisTemplate.opsForValue().get(key);//以店铺id做为key,这样就得到店铺的json字符串 //2.判断是否存在 if (StrUtil.isBlank(json)) { //3.存在直接返回 return null; } //4.命中需要先把json反序列化为对象 RedisData redisData JSONUtil.toBean(json, RedisData.class); R r JSONUtil.toBean((JSONObject) redisData.getData(), type); LocalDateTime expireTime redisData.getExpireTime(); //5.判断是否过期 if (expireTime.isAfter(LocalDateTime.now())){ //5.1.未过期直接返回店铺信息 return r; } //5.2.已过期需要缓存重建 //6.缓存重建 //6.1获取互斥锁 String lockKey LOCK_SHOP_KEY id; boolean isLock tryLock(lockKey); //6.2判断是否获取锁成功 if (isLock){ //TODO 6.3成功开启独立线程实现缓存重建 //注意获取锁成功应该再次检测redis缓存是否过期做DoubleCheck。如果存在则无需重建缓存 CACHE_REBUILD_EXECUTOR.submit(() -{ try { //重建缓存 //查询数据库 R r1 dbFallback.apply(id); //写入Redis this.setWithLogicalExpire(key,r1,time,unit); } catch (Exception e) { throw new RuntimeException(e); }finally { //释放锁 unLock(lockKey); } }); } //6.4.失败返回过期商铺信息 return r; }Shop shop cacheClient.queryWithLogicalExpire (CACHE_SHOP_KEY, id,Shop.class,this::getById,20L,TimeUnit.SECONDS);
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻