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


redis
1 篇文章0 订阅
订阅专栏
文章目录
前言
一、技术沉淀
1.模板模式
(1)介绍
(2)场景模拟
(2)场景解析
二、需求结合代码
1. 分析
2. 代码实现
(1). 定义中间人
(2). 定义仓库
三、最终实现效果
1. User服务配置中间人管理RedisKey:
2. 最终使用
总结
前言
现阶段公司后端架构中缓存模块代码大量冗余,各个服务都有各自的缓存模块,并且功能一致,由于之前没有合适的方案提取Client方法,因此一直没有进行优化

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

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

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

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

二、需求结合代码
1. 分析
是否可以让所有的服务从自己去拿货改为定一个中间人去拿货?
如果拿货的那个角色可以让中间人去执行,那么仓库是否也可以统一管理呢?因为不统一管理的话,中间人需要拿着拿着商家的钥匙跑遍不同的仓库,效率上会更加低下。
如何让中间人拿到商家的身份卡去通过保安的识别?
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * @className: ObjectVo
 * @description: 自定义函数接口
 * @author: 路过人间的姜先生
 * @date: 2022/9/1
 */
@FunctionalInterface
public interface ObjectVo<T> {

  T get();
}

1
2
3
4
5
6
7
8
9
10
11
12
(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
三、最终实现效果
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());
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
总结
代码架构优化是一个剥丝抽茧的过程,希望我的帖子对各位有帮助。
————————————————
版权声明:本文为CSDN博主「路过人间的姜先生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_44958794/article/details/126624632

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值