文章目录

  • 前言
  • 一、技术沉淀
    • 1.模板模式
      • (1)介绍
      • (2)场景模拟
        • (2)场景解析
  • 二、需求结合代码
    • 1. 分析
    • 2. 代码实现
      • (1). 定义中间人
      • (2). 定义仓库
  • 三、最终实现效果
    • 1. User服务配置中间人管理RedisKey:
    • 2. 最终使用
  • 总结

前言

现阶段公司后端架构中缓存模块代码大量冗余,各个服务都有各自的缓存模块,并且功能一致,由于之前没有合适的方案提取Client方法,因此一直没有进行优化


提示:以下是本篇文章正文内容,下面案例可供参考

一、技术沉淀

1.模板模式

(1)介绍

模板方法模式是一种行为设计模式, 它在父类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。主要要用来复用代码

(2)场景模拟

在我们的系统中把十三个重要服务比喻为一个商家,每个商家都会有一些营销活动,活动的时候赠送礼物,这十三个商家要用的赠礼都放在各自的仓库里。不过每个商家来拿货的时候,各家门口的保安都需要确定商家的身份。

(2)场景解析

在这个场景中,我们系统的每个服务就是商家,仓库就是对应的Redis缓存,而那个门口确定身份的保安就是请求Redis所需要的Key。在这个场景中每个微服务都需要去Redis缓存中拿数据,虽然不同的服务拿出来数据有可能不同,可是本质上来分析就是服务带着Key去请求Redis

二、需求结合代码

1. 分析
  1. 是否可以让所有的服务从自己去拿货改为定一个中间人去拿货?
  2. 如果拿货的那个角色可以让中间人去执行,那么仓库是否也可以统一管理呢?因为不统一管理的话,中间人需要拿着拿着商家的钥匙跑遍不同的仓库,效率上会更加低下。
  3. 如何让中间人拿到商家的身份卡去通过保安的识别?
2. 代码实现
(1). 定义中间人
/*** 中间人VO类,继承了[AbstractRedisGet]仓库出入权* <T> 为泛型是因为为了适配各个服务数据* @author 路过人间的姜先生*/
@Getter
@Setter
public abstract class AbstractDefaultVo<T> extends AbstractRedisGet {private String id;private String createUserId;private String updateUserId;private String createTime;private String updateTime;
}
/*** @className: ObjectVo* @description: 自定义函数接口* @author: 路过人间的姜先生* @date: 2022/9/1*/
@FunctionalInterface
public interface ObjectVo<T> {T get();
}
(2). 定义仓库
/*** @className: AbstractRedisGet* @description: 统一管理获取缓存的方法(缓存仓库入口)* @author: 路过人间的姜先生* @date: 2022/8/26*/
@Slf4j
@Component
@Data
public abstract class AbstractRedisGet {@JSONField(serialize = false)private String redisKey;// 需要各个服务VO去实现的 build()方法,把对应的 redisKey 在接口里面进行赋值public abstract void build();/*** 获取Redis的方法** @param: Supplier<T> supplier java8中自带的生产型接口* @author: 路过人间的姜先生* @date: 2022/8/26*/public <T> T getObjectVo(Supplier<T> supplier) {initializeBuild();Class aClass = parametBuilder();if (aClass != null) {// 获取缓中的对象return getT(redisKey, aClass, null, supplier);}return null;}/*** 获取Redis的方法,重载上面的获取Redis方法,在上面方法的基础上,* 可以重新设置缓存失效时间*  * @author: 路过人间的姜先生* @date: 2022/8/26*/public <T> T getObjectVo(Integer seconds, Supplier<T> supplier) {initializeBuild();Class aClass = parametBuilder();if (aClass != null) {// 获取缓中的对象return getT(redisKey, aClass, seconds, supplier);}return null;}/*** 获取Redis的方法,其中只需要把需要执行的查询方法传入,就可以执行缓存查询的方法,如果缓存中没有的情况下会去执行您传入的方法** <p>和之前方法不同在于,可以使用支持任意类对象** <p>author: 路过人间的姜先生** <p>2022/8/26*/public <T> T getObject(ObjectVo<T> supplier) {initializeBuild();// 获取缓中的对象return getT(this.redisKey, Object.class, null, supplier);}/*** 获取Redis的方法,其中只需要把需要执行的查询方法传入,就可以执行缓存查询的方法,如果缓存中没有的情况下会去执行您传入的方法** <p>和之前方法不同在于,可以使用支持任意类对象 , 并且不需要实现改抽象类,可以用作扩展** <p>author: 路过人间的姜先生** <p>2022/8/26*/public <T> T getObject(Class clazz, Integer seconds, ObjectVo<T> supplier) {initializeBuild();// 获取缓中的对象return getT(this.redisKey, clazz, seconds, supplier);}/*** 通过key删除缓存** @author: 路过人间的姜先生* @date: 2022/8/26*/public void deleteCache() {initializeBuild();if (StringUtils.isNotBlank(redisKey) && RedisUtils.isCached(redisKey)) {RedisUtils.expire(redisKey, 1);}}/*** 构建处理Build方法,并且获取泛型实例** @return: void* @author: 路过人间的姜先生* @date: 2022/8/31*/private Class parametBuilder() {if (StringUtils.isNotBlank(redisKey)) {// 获取泛型实列ParameterizedType ptype = (ParameterizedType) this.getClass().getGenericSuperclass();Class clazz = (Class<T>) ptype.getActualTypeArguments()[0];return clazz;}return null;}/*** 构建处理Build方法** @date: 2022/9/1*/private void initializeBuild() {// 处理build方法,获取到 redisKeytry {Method build = this.getClass().getMethod("build");build.invoke(this);} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {log.error("Vo对象中的build方法不存在");}}/*** 返回缓存中的对象** @author: 路过人间的姜先生* @date: 2022/8/30*/private <T> T getT(String key, Class c, Integer seconds, Supplier<T> supplier) {try {return (T)Optional.ofNullable(RedisUtils.getBean(key, c)).map(e -> {RedisUtils.expire(key, seconds != null ? seconds : 86400);return e;}).orElseGet(() ->Optional.ofNullable(get(supplier)).map(e -> {RedisUtils.saveBean(key, e, seconds != null ? seconds : 86400);return e;}).orElseGet(() -> null));} catch (Exception e) {log.error("查询缓存失败={}", e.getMessage());return null;}}/*** 返回缓存中的对象 == 支持java基本对象类型** @author: 路过人间的姜先生* @date: 2022/8/30*/public <T> T getT(String key, Class c, Integer seconds, ObjectVo<T> supplier) {try {return (T)Optional.ofNullable(RedisUtils.getBean(key, c)).map(e -> {RedisUtils.expire(key, seconds != null ? seconds : 86400);return e;}).orElseGet(() ->Optional.ofNullable(supplier.get()).map(e -> {RedisUtils.saveBean(key, e, seconds != null ? seconds : 86400);return e;}).orElseGet(() -> null));} catch (Exception e) {log.error("查询缓存失败={}", e.getMessage());return null;}}/*** 单独处理查询服务的逻辑,并且进行异常处理** @author: 路过人间的姜先生* @date: 2022/8/26*/private <T> T get(Supplier<T> supplier) {try {return supplier.get();} catch (Exception e) {log.error("查询服务失败={}", e.getMessage());}return null;}
}

三、最终实现效果

1. User服务配置中间人管理RedisKey:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class UserVo extends AbstractDefaultVo<UserVo>{public UserVo(String id) {super.setId(id);}@Overridepublic void build() {// 定义Rediskey , 在对对象进行初始化并且传入ID的时候,会直接给XcbRedisUtils的redisKey字段赋值super.setRedisKey(UserRedisKey.USER_USERVO.getName() + super.getId());}
}
2. 最终使用
 // 查询缓存UserVo userVo =new UserVo(userId).getObjectVo(() -> {查询方法});
// 删除缓存
new UserVo(userId).deleteCache();// 自定义查询缓存方法AbstractRedisGet redisGet =new AbstractRedisGet() {@Overridepublic void build() {}};List<任意对象> list =redisGet.getT(key,List.class,15,() -> {查询方法});

总结

代码架构优化是一个剥丝抽茧的过程,希望我的帖子对各位有帮助。

通用Redis查询工具类,结合函数编程和设计模式相关推荐

  1. java redis remove_最全的Java操作Redis的工具类

    RedisUtil 当前版本:1.1 增加更全的方法,对以前的部分方法进行了规范命名,请放心替换成新版本. 介绍 最全的Java操作Redis的工具类,使用StringRedisTemplate实现, ...

  2. 免费天气查询工具类源码,开箱即用,根据中国气象局API编写。高效稳定

    文章目录 引言 相关依赖 WeatherUtil工具类代码 http工具类 测试 引言 使用Java语言,根据中国气象局API编写的查询天气工具类,代码引入就能用,代码中对异常做了处理,无论是否查询成 ...

  3. SpringBoot中操作spring redis的工具类

    场景 SpringBoot+Vue+Redis实现前后端分离的字典缓存机制: https://blog.csdn.net/badao_liumang_qizhi/article/details/108 ...

  4. 分享一个nodejs中koa操作redis的工具类 基于 ioredis

    分享一个node 操作redis的工具类 基于ioredis redis.js const config = require(':config/server.base.config'); const ...

  5. NC6 查询工具类 QueryUtil.java

    NC6 查询工具类 package nc.impl.am.db;import java.util.ArrayList; import java.util.Collections; import jav ...

  6. java redis缓存工具类_redis工具类-JedisUtil

    redis连接的工具类 1.java中的redis java中,使用redis不会将其当作数据库来使用,更多的是作为缓存或者是消息中间件来使用.在用作缓存时,我们需要使用第三方提供的jar包来进行开发 ...

  7. java rowmapper 通用实现_springmvc工具类封装RowMapper详解

    springmvc通常是先写实体,在数据库查询,最后增删改差,最感觉代码很冗余,自己在封装了一下. 常见的结构是: entity:如 package com.liuxinquan.entiry; /* ...

  8. hbase 按时刻查询_Hbase查询工具类,根据时间查询数据

    1,需求:已知空气监测数据在hbase中存储,要求按照时间,查询citycode为110000(北京)一个月的数据,数据为每日的监测数据 ID ,CITYCODE,SO2 ,CO,NO2 ,O3, P ...

  9. Flutter - 一个fultter练习项目(仿写微信UI、实现一些常用效果、封装通用组件和工具类)

    demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新,请前往github查看最新代码 pwd:123456 代码不定期更新 注:Flut ...

最新文章

  1. 函数声明末尾的“ const”是什么意思? [重复]
  2. 孕妇可以使用计算机,【电脑对孕妇有影响吗】电脑对孕妇的危害,孕妇能玩电脑吗 - 妈妈网百科...
  3. @RequestParam 注解的使用——Spring系列知识学习笔记
  4. .NET中生成动态验证码
  5. .NET应用迁移到.NET Core(三)从商业角度看移植过程
  6. 获取两个数据的交集_MySQL交集和差集的实现方法
  7. 使用WildFly 8在Java EE7中自举Apache Camel
  8. Python-100 练习题 02
  9. python开发框架 代码生成_我的第一个python web开发框架(31)——定制ORM(七)...
  10. beyond compare类似软件_你用过最好用的截图软件是哪一款
  11. C#+ArcEngine中com对象的释放问题
  12. 球形天空盒php,unity3d天空盒
  13. mysql千万测试表生成,随机id、username、age、sex、create_time
  14. mysql error1682_mysql5.7报错 1546、1577和1682问题分析
  15. (Android+Qt最小系统设计方案)RK3288核心板设计之软件开发环境搭建(4.0)
  16. 学生DW静态网页设计—西安旅游-高质量(9页) HTML+CSS+JavaScript 学生DW网页设计
  17. MySQL实现分数排名问题
  18. 微信接口返回的状态码
  19. 视频教程-Android Material Design 新控件-Android
  20. 使用 FFmpeg 转换视频/音频格式 | 开源 免费 | 不用套壳软件

热门文章

  1. 学习日志--1.html
  2. 页面提交成功后,弹窗提示
  3. Arnold阿诺德渲染器:C4DtoA for Cinema4D R20 for Mac
  4. 微信小程序的项目缓存路径
  5. 透析阿里3亿元投资的如涵:孵化张大奕,吸金但苦逼
  6. 用python做算法需要哪些技能_成为一名CV算法工程师,你需要具备哪些能力?
  7. 爬取《喜马拉雅》音频排行榜
  8. 环信群列表 php,一行代码实现群聊头像(用环信仿微信群聊头像)
  9. Matter理论介绍-通用-1-04:桥接器-其他功能
  10. 跨境电商的支付有段有哪些?