Spring Boot 项目实战(四)集成 Redis

一、前言

上篇介绍了接口文档工具 Swagger 及项目监控工具 JavaMelody 的集成过程,使项目更加健壮。在 JAVA Web 项目某些场景中,我们需要用缓存解决如热点数据访问的性能问题,业界常用的中间件如 Memcached 、 Redis 等。相比 Memcached ,Redis 支持更丰富的数据结构。本篇将主要介绍在 Spring Boot 中集成 Redis 的过程。


二、集成 Redis

在 Spring Boot 中使用 Redis 有两种方式:

  • 基于 RedisTemplate 类,该类是 Spring Data 提供的工具,可以直接注入使用。
  • 基于 Jedis,Jedis 是 Redis 官方推荐的面向 JAVA 的客户端。

本文将介绍第一种使用方式。

2.1 引入依赖包

其实 Spring Boot 提供的父工程中已经包含了所依赖的 Redis jar 包,我们只需在相应模块引入即可。第一篇我们已经提到过 demo-common 层是公用组件层,那么 Redis 相关的声明及配置应该在该层定义。于是乎在 demo-common 层的 pom 文件中引入 Redis 的依赖包。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.2 RedisTemplate 的自动配置

其实我们现在就可以在项目中注入 RedisTemplate 并使用了,至于原因,首先看下「 RedisAutoConfiguration 」类的源码:

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
@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}

@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}

从源码可以看出,Spring Boot 会自动帮我们生成了一个 RedisTemplate 及一个 StringRedisTemplate ,但是这个 RedisTemplate 的泛型是 <Object, Object> ,如果我们直接使用就需要处理各种类型转换。所以为了方便使用,我们需要自定义一个泛型为 <String, Object> 的 RedisTemplate 。
而 @ConditionalOnMissingBean 注解的作用是在当前 Spring 上下文中不存在某个对象时,才会自动实例化一个 Bean 。因此我们可以自定义 RedisTemplate 从而替代默认的。

2.2 自定义 Redis 配置类

Spring Data 提供了若干个 Serializer ,主要包括:

  • JdkSerializationRedisSerializer — 使用 JAVA 自带的序列化机制将对象序列化为一个字符串
  • OxmSerializer — 将对象序列化为 XML 字符串
  • Jackson2JsonRedisSerializer — 将对象序列化为 JSON 字符串

其中 RedisTemplate 默认的序列化方式是 Jdk ,虽然是效率比较高但是序列化结果的字符串是最长的。而 JSON 由于其数据格式的紧凑型,序列化结果的字符串是最小的,即占用的内存最小。所以我们选择用 Jackson 替代默认的 Jdk 方式。

① 首先在项目父 pom 文件中定义 Jackson 的版本号且声明 Jackson 依赖。

1
2
3
4
<properties>
...省略其余部分...
<jackson.version>2.9.4</jackson.version>
</properties>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependencyManagement>
<dependencies>
...省略其余部分...
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

② 其次在 demo-common 层的 pom 文件中添加上述 Jackson 依赖。

1
2
3
4
5
6
7
8
9
10
11
<dependencies>
...省略其余部分...
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependencies>

③ 最后在 demo-common 层创建 com.example.demo.common 包,添加 Redis 目录并在其中创建 RedisConfig 配置类。

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
package com.example.demo.common.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
* @author linjian
* @date 2019/3/2
*/
@Configuration
public class RedisConfig {

@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.registerModule(new JavaTimeModule());
objectMapper.findAndRegisterModules();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

// 使用 Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(factory);
// key 采用 String 的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
// hash 的 key 也采用 String 的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value 序列化方式采用 jackson
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash 的 value 序列化方式采用 jackson
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

2.3 自定义 Redis 工具类

直接使用 RedisTemplate 操作 Redis 需要很多额外的代码,最好封装成一个工具类,使用时直接注入。
① 定义一个常用的缓存时间常量类

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
package com.example.demo.common.redis;

/**
* @author linjian
* @date 2019/3/2
*/
public class CacheTime {

/**
* 缓存时效 5秒钟
*/
public static int CACHE_EXP_FIVE_SECONDS = 5;

/**
* 缓存时效 1分钟
*/
public static int CACHE_EXP_MINUTE = 60;

/**
* 缓存时效 5分钟
*/
public static int CACHE_EXP_FIVE_MINUTES = 60 * 5;

/**
* 缓存时效 10分钟
*/
public static int CACHE_EXP_TEN_MINUTES = 60 * 10;

/**
* 缓存时效 15分钟
*/
public static int CACHE_EXP_QUARTER_MINUTES = 60 * 15;

/**
* 缓存时效 60分钟
*/
public static int CACHE_EXP_HOUR = 60 * 60;

/**
* 缓存时效 12小时
*/
public static int CACHE_EXP_HALF_DAY = 12 * 60 * 60;

/**
* 缓存时效 1天
*/
public static int CACHE_EXP_DAY = 3600 * 24;

/**
* 缓存时效 1周
*/
public static int CACHE_EXP_WEEK = 3600 * 24 * 7;

/**
* 缓存时效 1月
*/
public static int CACHE_EXP_MONTH = 3600 * 24 * 30 * 7;

/**
* 缓存时效 永久
*/
public static int CACHE_EXP_FOREVER = 0;
}

② 定义工具类

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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
package com.example.demo.common.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* @author linjian
* @date 2019/3/2
*/
@Component
public class RedisClient {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 根据key 获取剩余过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long ttl(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}

/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean exists(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}

/**
* 模糊匹配批量删除
*
* @param pattern 匹配的前缀
*/
public void deleteByPattern(String pattern) {
Set<String> keys = redisTemplate.keys(pattern);
if (!CollectionUtils.isEmpty(keys)) {
redisTemplate.delete(keys);
}
}

/**
* 设置指定 key 的值
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time == CacheTime.CACHE_EXP_FOREVER) {
redisTemplate.opsForValue().set(key, value);
} else {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 获取指定 key 的值
*
* @param key 键
* @return
*/
@SuppressWarnings("unchecked")
public <T> T get(String key) {
return key == null ? null : (T) redisTemplate.opsForValue().get(key);
}

/**
* 将 key 中储存的数字值递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta <= 0) {
throw new IllegalArgumentException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}

/**
* 将 key 中储存的数字值递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta <= 0) {
throw new IllegalArgumentException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}

/**
* 将哈希表 key 中的字段 field 的值设为 value
*
* @param key 键
* @param field 字段
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String field, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, field, value);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 同时将多个 field-value (域-值)对设置到哈希表 key 中
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 删除一个或多个哈希表字段
*
* @param key 键
* @param field 字段 可以多个
*/
public void hdel(String key, Object... field) {
redisTemplate.opsForHash().delete(key, field);
}

/**
* 获取存储在哈希表中指定字段的值
*
* @param key 键
* @param field 字段
* @return
*/
public <T> T hget(String key, String field) {
return (T) redisTemplate.opsForHash().get(key, field);
}

/**
* 获取在哈希表中指定 key 的所有字段和值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}


/**
* 查看哈希表 key 中,指定的字段是否存在
*
* @param key 键
* @param field 字段
* @return true 存在 false不存在
*/
public boolean hexists(String key, String field) {
return redisTemplate.opsForHash().hasKey(key, field);
}

/**
* 获取哈希表中字段的数量
*
* @param key 键
* @return 字段数量
*/
public long hlen(String key) {
try {
return redisTemplate.opsForHash().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}

/**
* 向集合添加一个或多个成员
*
* @param key 键
* @param time 时间(秒)
* @param values 成员 可以是多个
* @return 成功个数
*/
public long sadd(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}

/**
* 移除集合中一个或多个成员
*
* @param key 键
* @param values 成员 可以是多个
* @return 移除的个数
*/
public long srem(String key, Object... values) {
try {
return redisTemplate.opsForSet().remove(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}

/**
* 返回集合中的所有成员
*
* @param key 键
* @return 成员列表
*/
public <T> Set<T> smembers(String key) {
try {
return (Set<T>) redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 判断 member 元素是否是集合 key 的成员
*
* @param key 键
* @param member 成员
* @return true 存在 false不存在
*/
public boolean sismember(String key, Object member) {
try {
return redisTemplate.opsForSet().isMember(key, member);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


/**
* 获取集合的成员数
*
* @param key 键
* @return 成员数
*/
public long slen(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}

/**
* 在列表头部添加一个值
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return boolean
*/
public boolean lpush(String key, Object value, long time) {
try {
redisTemplate.opsForList().leftPush(key, value);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 在列表头部添加多个值
*
* @param key 键
* @param values 值
* @param time 时间(秒)
* @return boolean
*/
public boolean lpush(String key, List<Object> values, long time) {
try {
redisTemplate.opsForList().leftPushAll(key, values);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 在列表尾部添加一个值
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return boolean
*/
public boolean rpush(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 在列表尾部添加多个值
*
* @param key 键
* @param values 值
* @param time 时间(秒)
* @return boolean
*/
public boolean rpush(String key, List<Object> values, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, values);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 移除列表元素
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lrem(String key, long count, Object value) {
try {
return redisTemplate.opsForList().remove(key, count, value);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}

/**
* 通过索引设置列表元素的值
*
* @param key 键
* @param index 索引
* @param value 值
* @return boolean
*/
public boolean lset(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 获取列表指定范围内的元素
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return 元素列表
*/
@SuppressWarnings("unchecked")
public <T> List<T> lrange(String key, long start, long end) {
try {
return (List<T>) redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 通过索引获取列表中的元素
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lindex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 获取列表长度
*
* @param key 键
* @return 列表长度
*/
public long llen(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}

/**
* 向有序集合添加一个成员,或者更新已存在成员的分数
*
* @param key 键
* @param time 时间(秒)
* @param member 成员
* @param score 分数
* @return
*/
public boolean zadd(String key, long time, Object member, double score) {
try {
boolean ret = redisTemplate.opsForZSet().add(key, member, score);
if (time != CacheTime.CACHE_EXP_FOREVER) {
expire(key, time);
}
return ret;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 移除有序集合中的一个或多个成员
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long zrem(String key, Object... values) {
try {
return redisTemplate.opsForZSet().remove(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}

/**
* 通过索引区间返回有序集合成指定区间内的成员 分数从低到高
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return 成员集合
*/
public Set<Object> zrange(String key, long start, long end) {
try {
return redisTemplate.opsForZSet().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 通过索引区间返回有序集合成指定区间内的成员 分数从高到低
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return 成员集合
*/
public Set<Object> zrevrange(String key, long start, long end) {
try {
return redisTemplate.opsForZSet().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

/**
* 返回有序集合中某个成员的分数值
*
* @param key 键
* @param member 成员
* @return 分数值
*/
public double zscore(String key, Object member) {
try {
return redisTemplate.opsForZSet().score(key, member);
} catch (Exception e) {
e.printStackTrace();
return 0.0;
}
}

/**
* 判断有序集合中某个成员是否存在
*
* @param key 键
* @param member 成员
* @return true 存在 false不存在
*/
public boolean zexist(String key, Object member) {
try {
return null != redisTemplate.opsForZSet().score(key, member);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

/**
* 获取有序集合的成员数
*
* @param key 键
* @return 成员数
*/
public long zlen(String key) {
try {
return redisTemplate.opsForZSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0L;
}
}
}

2.4 添加 Redis 常用配置项

在 application.properties 文件中的添加 Redis 相关的配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 数据库索引(默认为0)
spring.redis.database = 1
# 服务器地址
spring.redis.host = 127.0.0.1
# 服务器连接端口
spring.redis.port = 6379
# 服务器连接密码(默认为空)
spring.redis.password =
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait = -1
# 连接超时时间(毫秒)
spring.redis.timeout = 3000
# 连接池最大连接数
spring.redis.jedis.pool.max-active = 8
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle = 8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle = 1

2.5 Redis 缓存测试

① 首先在 DemoService 中注入 RedisClient ,修改 test 方法将 user 对象以 user:1 为键存放到 Redis 中。

Redis 开发规范:https://yq.aliyun.com/articles/531067

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
package com.example.demo.biz.service.impl;

import com.example.demo.biz.service.DemoService;
import com.example.demo.common.redis.CacheTime;
import com.example.demo.common.redis.RedisClient;
import com.example.demo.dao.entity.UserDO;
import com.example.demo.dao.mapper.business.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* @author linjian
* @date 2019/1/15
*/
@Service
public class DemoServiceImpl implements DemoService {

@Autowired
private UserMapper userMapper;

@Autowired
private RedisClient redisClient;

@Override
public String test() {
UserDO user = userMapper.selectById(1);
redisClient.set("user:1", user, CacheTime.CACHE_EXP_FIVE_MINUTES);
return user.toString();
}
}

② 之后使用 Redis Desktop Manager 客户端连接 Redis 服务器,选择数据库「 1 」,查看刚存放的缓存。
SpringBoot_4_1.png


三、结语

至此 Spring Boot 集成 Redis 的具体步骤介绍完毕,我们自定义了 Redis 的序列化方式,并通过一个简单的例子测试了 Redis 的可用性,相关代码已同步至 GitHub 。