一、前言

上篇介绍了接口文档工具 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. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>

2.2 RedisTemplate 的自动配置

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

  1. @Configuration
  2. @ConditionalOnClass({RedisOperations.class})
  3. @EnableConfigurationProperties({RedisProperties.class})
  4. @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
  5. public class RedisAutoConfiguration {
  6. public RedisAutoConfiguration() {
  7. }
  8. @Bean
  9. @ConditionalOnMissingBean(
  10. name = {"redisTemplate"}
  11. )
  12. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
  13. RedisTemplate<Object, Object> template = new RedisTemplate();
  14. template.setConnectionFactory(redisConnectionFactory);
  15. return template;
  16. }
  17. @Bean
  18. @ConditionalOnMissingBean
  19. public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
  20. StringRedisTemplate template = new StringRedisTemplate();
  21. template.setConnectionFactory(redisConnectionFactory);
  22. return template;
  23. }
  24. }

从源码可以看出,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. <properties>
  2. ...省略其余部分...
  3. <jackson.version>2.9.4</jackson.version>
  4. </properties>
  1. <dependencyManagement>
  2. <dependencies>
  3. ...省略其余部分...
  4. <dependency>
  5. <groupId>com.fasterxml.jackson.core</groupId>
  6. <artifactId>jackson-databind</artifactId>
  7. <version>${jackson.version}</version>
  8. </dependency>
  9. <dependency>
  10. <groupId>com.fasterxml.jackson.datatype</groupId>
  11. <artifactId>jackson-datatype-jsr310</artifactId>
  12. <version>${jackson.version}</version>
  13. </dependency>
  14. </dependencies>
  15. </dependencyManagement>

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

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

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

  1. package com.example.demo.common.redis;
  2. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  3. import com.fasterxml.jackson.annotation.PropertyAccessor;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import com.fasterxml.jackson.databind.SerializationFeature;
  6. import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.data.redis.connection.RedisConnectionFactory;
  10. import org.springframework.data.redis.core.RedisTemplate;
  11. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  12. import org.springframework.data.redis.serializer.StringRedisSerializer;
  13. /**
  14. * @author linjian
  15. * @date 2019/3/2
  16. */
  17. @Configuration
  18. public class RedisConfig {
  19. @Bean
  20. @SuppressWarnings("all")
  21. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  22. ObjectMapper objectMapper = new ObjectMapper();
  23. objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  24. objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  25. objectMapper.registerModule(new JavaTimeModule());
  26. objectMapper.findAndRegisterModules();
  27. objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
  28. // 使用 Jackson2JsonRedisSerialize 替换默认序列化
  29. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  30. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  31. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  32. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
  33. redisTemplate.setConnectionFactory(factory);
  34. // key 采用 String 的序列化方式
  35. redisTemplate.setKeySerializer(stringRedisSerializer);
  36. // hash 的 key 也采用 String 的序列化方式
  37. redisTemplate.setHashKeySerializer(stringRedisSerializer);
  38. // value 序列化方式采用 jackson
  39. redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
  40. // hash 的 value 序列化方式采用 jackson
  41. redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
  42. redisTemplate.afterPropertiesSet();
  43. return redisTemplate;
  44. }
  45. }

2.3 自定义 Redis 工具类

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

① 定义一个常用的缓存时间常量类

  1. package com.example.demo.common.redis;
  2. /**
  3. * @author linjian
  4. * @date 2019/3/2
  5. */
  6. public class CacheTime {
  7. /**
  8. * 缓存时效 5秒钟
  9. */
  10. public static int CACHE_EXP_FIVE_SECONDS = 5;
  11. /**
  12. * 缓存时效 1分钟
  13. */
  14. public static int CACHE_EXP_MINUTE = 60;
  15. /**
  16. * 缓存时效 5分钟
  17. */
  18. public static int CACHE_EXP_FIVE_MINUTES = 60 * 5;
  19. /**
  20. * 缓存时效 10分钟
  21. */
  22. public static int CACHE_EXP_TEN_MINUTES = 60 * 10;
  23. /**
  24. * 缓存时效 15分钟
  25. */
  26. public static int CACHE_EXP_QUARTER_MINUTES = 60 * 15;
  27. /**
  28. * 缓存时效 60分钟
  29. */
  30. public static int CACHE_EXP_HOUR = 60 * 60;
  31. /**
  32. * 缓存时效 12小时
  33. */
  34. public static int CACHE_EXP_HALF_DAY = 12 * 60 * 60;
  35. /**
  36. * 缓存时效 1天
  37. */
  38. public static int CACHE_EXP_DAY = 3600 * 24;
  39. /**
  40. * 缓存时效 1周
  41. */
  42. public static int CACHE_EXP_WEEK = 3600 * 24 * 7;
  43. /**
  44. * 缓存时效 1月
  45. */
  46. public static int CACHE_EXP_MONTH = 3600 * 24 * 30 * 7;
  47. /**
  48. * 缓存时效 永久
  49. */
  50. public static int CACHE_EXP_FOREVER = 0;
  51. }

② 定义工具类

  1. package com.example.demo.common.redis;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.data.redis.core.RedisTemplate;
  4. import org.springframework.stereotype.Component;
  5. import org.springframework.util.CollectionUtils;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Set;
  9. import java.util.concurrent.TimeUnit;
  10. /**
  11. * @author linjian
  12. * @date 2019/3/2
  13. */
  14. @Component
  15. public class RedisClient {
  16. @Autowired
  17. private RedisTemplate<String, Object> redisTemplate;
  18. /**
  19. * 指定缓存失效时间
  20. *
  21. * @param key 键
  22. * @param time 时间(秒)
  23. * @return
  24. */
  25. public boolean expire(String key, long time) {
  26. try {
  27. if (time > 0) {
  28. redisTemplate.expire(key, time, TimeUnit.SECONDS);
  29. }
  30. return true;
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. return false;
  34. }
  35. }
  36. /**
  37. * 根据key 获取剩余过期时间
  38. *
  39. * @param key 键 不能为null
  40. * @return 时间(秒) 返回0代表为永久有效
  41. */
  42. public long ttl(String key) {
  43. return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  44. }
  45. /**
  46. * 判断key是否存在
  47. *
  48. * @param key 键
  49. * @return true 存在 false不存在
  50. */
  51. public boolean exists(String key) {
  52. try {
  53. return redisTemplate.hasKey(key);
  54. } catch (Exception e) {
  55. e.printStackTrace();
  56. return false;
  57. }
  58. }
  59. /**
  60. * 删除缓存
  61. *
  62. * @param key 可以传一个值 或多个
  63. */
  64. @SuppressWarnings("unchecked")
  65. public void del(String... key) {
  66. if (key != null && key.length > 0) {
  67. if (key.length == 1) {
  68. redisTemplate.delete(key[0]);
  69. } else {
  70. redisTemplate.delete(CollectionUtils.arrayToList(key));
  71. }
  72. }
  73. }
  74. /**
  75. * 模糊匹配批量删除
  76. *
  77. * @param pattern 匹配的前缀
  78. */
  79. public void deleteByPattern(String pattern) {
  80. Set<String> keys = redisTemplate.keys(pattern);
  81. if (!CollectionUtils.isEmpty(keys)) {
  82. redisTemplate.delete(keys);
  83. }
  84. }
  85. /**
  86. * 设置指定 key 的值
  87. *
  88. * @param key 键
  89. * @param value 值
  90. * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
  91. * @return true成功 false 失败
  92. */
  93. public boolean set(String key, Object value, long time) {
  94. try {
  95. if (time == CacheTime.CACHE_EXP_FOREVER) {
  96. redisTemplate.opsForValue().set(key, value);
  97. } else {
  98. redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
  99. }
  100. return true;
  101. } catch (Exception e) {
  102. e.printStackTrace();
  103. return false;
  104. }
  105. }
  106. /**
  107. * 获取指定 key 的值
  108. *
  109. * @param key 键
  110. * @return 值
  111. */
  112. @SuppressWarnings("unchecked")
  113. public <T> T get(String key) {
  114. return key == null ? null : (T) redisTemplate.opsForValue().get(key);
  115. }
  116. /**
  117. * 将 key 中储存的数字值递增
  118. *
  119. * @param key 键
  120. * @param delta 要增加几(大于0)
  121. * @return
  122. */
  123. public long incr(String key, long delta) {
  124. if (delta <= 0) {
  125. throw new IllegalArgumentException("递增因子必须大于0");
  126. }
  127. return redisTemplate.opsForValue().increment(key, delta);
  128. }
  129. /**
  130. * 将 key 中储存的数字值递减
  131. *
  132. * @param key 键
  133. * @param delta 要减少几(小于0)
  134. * @return
  135. */
  136. public long decr(String key, long delta) {
  137. if (delta <= 0) {
  138. throw new IllegalArgumentException("递减因子必须大于0");
  139. }
  140. return redisTemplate.opsForValue().increment(key, -delta);
  141. }
  142. /**
  143. * 将哈希表 key 中的字段 field 的值设为 value
  144. *
  145. * @param key 键
  146. * @param field 字段
  147. * @param value 值
  148. * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
  149. * @return true 成功 false失败
  150. */
  151. public boolean hset(String key, String field, Object value, long time) {
  152. try {
  153. redisTemplate.opsForHash().put(key, field, value);
  154. if (time != CacheTime.CACHE_EXP_FOREVER) {
  155. expire(key, time);
  156. }
  157. return true;
  158. } catch (Exception e) {
  159. e.printStackTrace();
  160. return false;
  161. }
  162. }
  163. /**
  164. * 同时将多个 field-value (域-值)对设置到哈希表 key 中
  165. *
  166. * @param key 键
  167. * @param map 对应多个键值
  168. * @param time 时间(秒)
  169. * @return true成功 false失败
  170. */
  171. public boolean hmset(String key, Map<String, Object> map, long time) {
  172. try {
  173. redisTemplate.opsForHash().putAll(key, map);
  174. if (time != CacheTime.CACHE_EXP_FOREVER) {
  175. expire(key, time);
  176. }
  177. return true;
  178. } catch (Exception e) {
  179. e.printStackTrace();
  180. return false;
  181. }
  182. }
  183. /**
  184. * 删除一个或多个哈希表字段
  185. *
  186. * @param key 键
  187. * @param field 字段 可以多个
  188. */
  189. public void hdel(String key, Object... field) {
  190. redisTemplate.opsForHash().delete(key, field);
  191. }
  192. /**
  193. * 获取存储在哈希表中指定字段的值
  194. *
  195. * @param key 键
  196. * @param field 字段
  197. * @return 值
  198. */
  199. public <T> T hget(String key, String field) {
  200. return (T) redisTemplate.opsForHash().get(key, field);
  201. }
  202. /**
  203. * 获取在哈希表中指定 key 的所有字段和值
  204. *
  205. * @param key 键
  206. * @return 对应的多个键值
  207. */
  208. public Map<Object, Object> hmget(String key) {
  209. return redisTemplate.opsForHash().entries(key);
  210. }
  211. /**
  212. * 查看哈希表 key 中,指定的字段是否存在
  213. *
  214. * @param key 键
  215. * @param field 字段
  216. * @return true 存在 false不存在
  217. */
  218. public boolean hexists(String key, String field) {
  219. return redisTemplate.opsForHash().hasKey(key, field);
  220. }
  221. /**
  222. * 获取哈希表中字段的数量
  223. *
  224. * @param key 键
  225. * @return 字段数量
  226. */
  227. public long hlen(String key) {
  228. try {
  229. return redisTemplate.opsForHash().size(key);
  230. } catch (Exception e) {
  231. e.printStackTrace();
  232. return 0L;
  233. }
  234. }
  235. /**
  236. * 向集合添加一个或多个成员
  237. *
  238. * @param key 键
  239. * @param time 时间(秒)
  240. * @param values 成员 可以是多个
  241. * @return 成功个数
  242. */
  243. public long sadd(String key, long time, Object... values) {
  244. try {
  245. Long count = redisTemplate.opsForSet().add(key, values);
  246. if (time != CacheTime.CACHE_EXP_FOREVER) {
  247. expire(key, time);
  248. }
  249. return count;
  250. } catch (Exception e) {
  251. e.printStackTrace();
  252. return 0L;
  253. }
  254. }
  255. /**
  256. * 移除集合中一个或多个成员
  257. *
  258. * @param key 键
  259. * @param values 成员 可以是多个
  260. * @return 移除的个数
  261. */
  262. public long srem(String key, Object... values) {
  263. try {
  264. return redisTemplate.opsForSet().remove(key, values);
  265. } catch (Exception e) {
  266. e.printStackTrace();
  267. return 0L;
  268. }
  269. }
  270. /**
  271. * 返回集合中的所有成员
  272. *
  273. * @param key 键
  274. * @return 成员列表
  275. */
  276. public <T> Set<T> smembers(String key) {
  277. try {
  278. return (Set<T>) redisTemplate.opsForSet().members(key);
  279. } catch (Exception e) {
  280. e.printStackTrace();
  281. return null;
  282. }
  283. }
  284. /**
  285. * 判断 member 元素是否是集合 key 的成员
  286. *
  287. * @param key 键
  288. * @param member 成员
  289. * @return true 存在 false不存在
  290. */
  291. public boolean sismember(String key, Object member) {
  292. try {
  293. return redisTemplate.opsForSet().isMember(key, member);
  294. } catch (Exception e) {
  295. e.printStackTrace();
  296. return false;
  297. }
  298. }
  299. /**
  300. * 获取集合的成员数
  301. *
  302. * @param key 键
  303. * @return 成员数
  304. */
  305. public long slen(String key) {
  306. try {
  307. return redisTemplate.opsForSet().size(key);
  308. } catch (Exception e) {
  309. e.printStackTrace();
  310. return 0L;
  311. }
  312. }
  313. /**
  314. * 在列表头部添加一个值
  315. *
  316. * @param key 键
  317. * @param value 值
  318. * @param time 时间(秒)
  319. * @return boolean
  320. */
  321. public boolean lpush(String key, Object value, long time) {
  322. try {
  323. redisTemplate.opsForList().leftPush(key, value);
  324. if (time != CacheTime.CACHE_EXP_FOREVER) {
  325. expire(key, time);
  326. }
  327. return true;
  328. } catch (Exception e) {
  329. e.printStackTrace();
  330. return false;
  331. }
  332. }
  333. /**
  334. * 在列表头部添加多个值
  335. *
  336. * @param key 键
  337. * @param values 值
  338. * @param time 时间(秒)
  339. * @return boolean
  340. */
  341. public boolean lpush(String key, List<Object> values, long time) {
  342. try {
  343. redisTemplate.opsForList().leftPushAll(key, values);
  344. if (time != CacheTime.CACHE_EXP_FOREVER) {
  345. expire(key, time);
  346. }
  347. return true;
  348. } catch (Exception e) {
  349. e.printStackTrace();
  350. return false;
  351. }
  352. }
  353. /**
  354. * 在列表尾部添加一个值
  355. *
  356. * @param key 键
  357. * @param value 值
  358. * @param time 时间(秒)
  359. * @return boolean
  360. */
  361. public boolean rpush(String key, Object value, long time) {
  362. try {
  363. redisTemplate.opsForList().rightPush(key, value);
  364. if (time != CacheTime.CACHE_EXP_FOREVER) {
  365. expire(key, time);
  366. }
  367. return true;
  368. } catch (Exception e) {
  369. e.printStackTrace();
  370. return false;
  371. }
  372. }
  373. /**
  374. * 在列表尾部添加多个值
  375. *
  376. * @param key 键
  377. * @param values 值
  378. * @param time 时间(秒)
  379. * @return boolean
  380. */
  381. public boolean rpush(String key, List<Object> values, long time) {
  382. try {
  383. redisTemplate.opsForList().rightPushAll(key, values);
  384. if (time != CacheTime.CACHE_EXP_FOREVER) {
  385. expire(key, time);
  386. }
  387. return true;
  388. } catch (Exception e) {
  389. e.printStackTrace();
  390. return false;
  391. }
  392. }
  393. /**
  394. * 移除列表元素
  395. *
  396. * @param key 键
  397. * @param count 移除多少个
  398. * @param value 值
  399. * @return 移除的个数
  400. */
  401. public long lrem(String key, long count, Object value) {
  402. try {
  403. return redisTemplate.opsForList().remove(key, count, value);
  404. } catch (Exception e) {
  405. e.printStackTrace();
  406. return 0;
  407. }
  408. }
  409. /**
  410. * 通过索引设置列表元素的值
  411. *
  412. * @param key 键
  413. * @param index 索引
  414. * @param value 值
  415. * @return boolean
  416. */
  417. public boolean lset(String key, long index, Object value) {
  418. try {
  419. redisTemplate.opsForList().set(key, index, value);
  420. return true;
  421. } catch (Exception e) {
  422. e.printStackTrace();
  423. return false;
  424. }
  425. }
  426. /**
  427. * 获取列表指定范围内的元素
  428. *
  429. * @param key 键
  430. * @param start 开始
  431. * @param end 结束 0 到 -1代表所有值
  432. * @return 元素列表
  433. */
  434. @SuppressWarnings("unchecked")
  435. public <T> List<T> lrange(String key, long start, long end) {
  436. try {
  437. return (List<T>) redisTemplate.opsForList().range(key, start, end);
  438. } catch (Exception e) {
  439. e.printStackTrace();
  440. return null;
  441. }
  442. }
  443. /**
  444. * 通过索引获取列表中的元素
  445. *
  446. * @param key 键
  447. * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
  448. * @return
  449. */
  450. public Object lindex(String key, long index) {
  451. try {
  452. return redisTemplate.opsForList().index(key, index);
  453. } catch (Exception e) {
  454. e.printStackTrace();
  455. return null;
  456. }
  457. }
  458. /**
  459. * 获取列表长度
  460. *
  461. * @param key 键
  462. * @return 列表长度
  463. */
  464. public long llen(String key) {
  465. try {
  466. return redisTemplate.opsForList().size(key);
  467. } catch (Exception e) {
  468. e.printStackTrace();
  469. return 0L;
  470. }
  471. }
  472. /**
  473. * 向有序集合添加一个成员,或者更新已存在成员的分数
  474. *
  475. * @param key 键
  476. * @param time 时间(秒)
  477. * @param member 成员
  478. * @param score 分数
  479. * @return
  480. */
  481. public boolean zadd(String key, long time, Object member, double score) {
  482. try {
  483. boolean ret = redisTemplate.opsForZSet().add(key, member, score);
  484. if (time != CacheTime.CACHE_EXP_FOREVER) {
  485. expire(key, time);
  486. }
  487. return ret;
  488. } catch (Exception e) {
  489. e.printStackTrace();
  490. return false;
  491. }
  492. }
  493. /**
  494. * 移除有序集合中的一个或多个成员
  495. *
  496. * @param key 键
  497. * @param values 值 可以是多个
  498. * @return 移除的个数
  499. */
  500. public long zrem(String key, Object... values) {
  501. try {
  502. return redisTemplate.opsForZSet().remove(key, values);
  503. } catch (Exception e) {
  504. e.printStackTrace();
  505. return 0L;
  506. }
  507. }
  508. /**
  509. * 通过索引区间返回有序集合成指定区间内的成员 分数从低到高
  510. *
  511. * @param key 键
  512. * @param start 开始
  513. * @param end 结束 0 到 -1代表所有值
  514. * @return 成员集合
  515. */
  516. public Set<Object> zrange(String key, long start, long end) {
  517. try {
  518. return redisTemplate.opsForZSet().range(key, start, end);
  519. } catch (Exception e) {
  520. e.printStackTrace();
  521. return null;
  522. }
  523. }
  524. /**
  525. * 通过索引区间返回有序集合成指定区间内的成员 分数从高到低
  526. *
  527. * @param key 键
  528. * @param start 开始
  529. * @param end 结束 0 到 -1代表所有值
  530. * @return 成员集合
  531. */
  532. public Set<Object> zrevrange(String key, long start, long end) {
  533. try {
  534. return redisTemplate.opsForZSet().range(key, start, end);
  535. } catch (Exception e) {
  536. e.printStackTrace();
  537. return null;
  538. }
  539. }
  540. /**
  541. * 返回有序集合中某个成员的分数值
  542. *
  543. * @param key 键
  544. * @param member 成员
  545. * @return 分数值
  546. */
  547. public double zscore(String key, Object member) {
  548. try {
  549. return redisTemplate.opsForZSet().score(key, member);
  550. } catch (Exception e) {
  551. e.printStackTrace();
  552. return 0.0;
  553. }
  554. }
  555. /**
  556. * 判断有序集合中某个成员是否存在
  557. *
  558. * @param key 键
  559. * @param member 成员
  560. * @return true 存在 false不存在
  561. */
  562. public boolean zexist(String key, Object member) {
  563. try {
  564. return null != redisTemplate.opsForZSet().score(key, member);
  565. } catch (Exception e) {
  566. e.printStackTrace();
  567. return false;
  568. }
  569. }
  570. /**
  571. * 获取有序集合的成员数
  572. *
  573. * @param key 键
  574. * @return 成员数
  575. */
  576. public long zlen(String key) {
  577. try {
  578. return redisTemplate.opsForZSet().size(key);
  579. } catch (Exception e) {
  580. e.printStackTrace();
  581. return 0L;
  582. }
  583. }
  584. }

2.4 添加 Redis 常用配置项

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

  1. # 数据库索引(默认为0)
  2. spring.redis.database = 1
  3. # 服务器地址
  4. spring.redis.host = 127.0.0.1
  5. # 服务器连接端口
  6. spring.redis.port = 6379
  7. # 服务器连接密码(默认为空)
  8. spring.redis.password =
  9. # 连接池最大阻塞等待时间(使用负值表示没有限制)
  10. spring.redis.pool.max-wait = -1
  11. # 连接超时时间(毫秒)
  12. spring.redis.timeout = 3000
  13. # 连接池最大连接数
  14. spring.redis.jedis.pool.max-active = 8
  15. # 连接池中的最大空闲连接
  16. spring.redis.jedis.pool.max-idle = 8
  17. # 连接池中的最小空闲连接
  18. 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. package com.example.demo.biz.service.impl;
  2. import com.example.demo.biz.service.DemoService;
  3. import com.example.demo.common.redis.CacheTime;
  4. import com.example.demo.common.redis.RedisClient;
  5. import com.example.demo.dao.entity.UserDO;
  6. import com.example.demo.dao.mapper.business.UserMapper;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.stereotype.Service;
  9. /**
  10. * @author linjian
  11. * @date 2019/1/15
  12. */
  13. @Service
  14. public class DemoServiceImpl implements DemoService {
  15. @Autowired
  16. private UserMapper userMapper;
  17. @Autowired
  18. private RedisClient redisClient;
  19. @Override
  20. public String test() {
  21. UserDO user = userMapper.selectById(1);
  22. redisClient.set("user:1", user, CacheTime.CACHE_EXP_FIVE_MINUTES);
  23. return user.toString();
  24. }
  25. }

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


三、结语

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

Spring Boot 项目实战(四)集成 Redis的更多相关文章

  1. Spring Boot 项目实战(五)集成 Dubbo

    一.前言 上篇介绍了 Redis 的集成过程,可用于解决热点数据访问的性能问题.随着业务复杂度的提高,单体应用越来越庞大,就好比一个类的代码行数越来越多,分而治之,切成多个类应该是更好的解决方法,所以 ...

  2. Spring Boot 项目学习 (四) Spring Boot整合Swagger2自动生成API文档

    0 引言 在做服务端开发的时候,难免会涉及到API 接口文档的编写,可以经历过手写API 文档的过程,就会发现,一个自动生成API文档可以提高多少的效率. 以下列举几个手写API 文档的痛点: 文档需 ...

  3. Spring Boot 1.5.4集成Redis

    本文示例源码,请看这里: 如何安装与配置Redis,请看这里 首先添加起步依赖: <dependency> <groupId>org.springframework.boot& ...

  4. Spring Boot 项目实战(六)集成 Apollo

    一.前言 上篇介绍了 Spring Boot 集成 Dubbo,使我们的系统打下了分布式的基础.随着程序功能的日益复杂,程序的配置日益增多:各种功能开关.参数配置.服务器地址等:对程序配置的期望值也越 ...

  5. Spring Boot 项目实战(三)集成 Swagger 及 JavaMelody

    一.前言 上篇介绍了 Logback 的集成过程,总体已经达到了基本可用的项目结构.本篇主要介绍两个常用工具,接口文档工具 Swagger .项目监控工具 JavaMelody 的集成步骤. 二.Sw ...

  6. spring boot 学习(十四)SpringBoot+Redis+SpringSession缓存之实战

    SpringBoot + Redis +SpringSession 缓存之实战 前言 前几天,从师兄那儿了解到EhCache是进程内的缓存框架,虽然它已经提供了集群环境下的缓存同步策略,这种同步仍然需 ...

  7. Spring Boot 项目实战(二)集成 Logback

    一.前言 上篇介绍了 Spring Boot Maven 多模块项目的搭建方法以及 MyBatis 的集成.通常在调试接口或者排查问题时我们主要借助于日志,一个设计合理的日志文件配置能大大降低我们的排 ...

  8. Spring Boot 2 实战:利用Redis的Geo功能实现查找附近的位置

    1. 前言 老板突然要上线一个需求,获取当前位置方圆一公里的业务代理点.明天上线!当接到这个需求的时候我差点吐血,这时间也太紧张了.赶紧去查相关的技术选型.经过一番折腾,终于在晚上十点完成了这个需求. ...

  9. Spring Boot 项目实战(一)Maven 多模块项目搭建

    一.前言 最近公司项目准备开始重构,框架选定为 Spring Boot ,本篇主要记录了在 IDEA 中搭建 Spring Boot Maven 多模块项目的过程. 二.软件及硬件环境 macOS S ...

随机推荐

  1. Linux Oracle bash: “sqlplus / as sysdba”: command not found 解决方法

    bash: sqlplus: command not found 解决方法 注:本文来源于 <   bash: sqlplus: command not found 解决方法   > 1: ...

  2. nginx实践(二)之静态资源web服务(浏览器缓存场景)

    配置语法-expires

  3. PHP中json_encode()使用须知,JSON数组和JSON对象

    ⊰ 偷偷的告诉你,这是一个很不谨慎就会踩得坑 ⊱  如下代码 (看了一下很简单,没毛病啊,老铁) $arr = array( '0'=>'a','1'=>'b','2'=>'c',' ...

  4. python并发编程之多线程1

    一多线程的概念介绍 threading模块介绍 threading模块和multiprocessing模块在使用层面,有很大的相似性. 二.开启多线程的两种方式 1.创建线程的开销比创建进程的开销小, ...

  5. LeetCode(109):有序链表转换二叉搜索树

    Medium! 题目描述: 给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1. 示例: ...

  6. Go如何正确的使用mysql driver

    具体文章查看: https://xiequan.info/go%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A1%AE%E7%9A%84%E4%BD%BF%E7%94%A8mysql- ...

  7. windows 7 下用git

    参考:http://my.oschina.net/longxuu/blog/141699

  8. 关于HTML或JS加密解密的七种方式

    本文一共介绍了七种方法:   一:最简单的加密解密   二:转义字符""的妙用   三:使用Microsoft出品的脚本编码器Script Encoder来进行编码    (自创简 ...

  9. 插件使用一表单验证一validation

    jquery-validation是一款前端经验js插件,可以验证必填字段.邮件.URL.数字范围等,在表单中应用非常广泛. 官方网站 https://jqueryvalidation.org/ 源码 ...

  10. error: each element of 'ext_modules' option must be an Extension instance or 2-tuple

    在编译cython扩展时出现. 解决办法: 必须先import setup再import extension,否则报错 from setuptools import setup from distut ...