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

3 篇文章 0 订阅
1 篇文章 0 订阅


前言

现阶段公司后端架构中缓存模块代码大量冗余,各个服务都有各自的缓存模块,并且功能一致,由于之前没有合适的方案提取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方法,获取到 redisKey
    try {
      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);
    }
    
    @Override
    public 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() {
          @Override
          public void build() {}
        };
    List<任意对象> list =
        redisGet.getT(
            key,
            List.class,
            15,
            () -> {查询方法});



总结

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路过人间的姜先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值