当前位置:   article > 正文

springboot 中文乱码_讲六:SpringBoot整合Redis详解

springboot整合redis乱码

1ad778e57dafd14181a8b1e2ce5f7d1b.png

1. Redis前言概述
1.1 Redis底层
1.2 Redis序列化问题
2. SpringBoot整合Redis实战

1. Redis前言概述

1.1 Redis底层

  1. 1. 在Spring2.x之后,原来使用的Jedis被替换为了lettuce。
  2. ⑴ jedis:底层采用的是直连,多个线程操作的话,是不安全的。
  3. - 如果想要避免不安全的这个因素,就是使用jedis pool的连接池,
  4. - 但是使用jedis pool的连接池由此带来的问题也非常大(线程数量多,service非常多等,很麻烦)
  5. ⑵ lettuce:底层采用netty,高性能网络框架,异步请求速度快,实例可以在多个线程之间共享。
  6. - 因此lettuce不存在线程不安全的情况,可以减少线程数量,就不需要去开连接池了。
  7. ⑶ 因此jedis更像:BIO模式,lettuce更像:NIO模式。

1.2 Redis序列化问题

  1. 1. 在Redis中,如存入的数据是中文,取出来可能会是乱码。
  2. ⑴ 关于Redis的中文乱码问题,这就涉及到RedisTemplate底层的数据类型问题
  3. 如-"图Ser1"。跟这几个序列化类型有关系。
  4. ⑵ 在RedisTemplate默认的序列化方式是JDK序列化,JDK序列化就会让我们的字符串
  5. 转义(这就是造成乱码的原因),因此我们可能会使用Json来序列化实现中文乱码的解决。
  6. ⑶ 所以就需要我们去自定义一个配置类,不使用Redis底层默认的JDK序列化配置方式。
  7. - 因此Redis官方表示:如果我们自己写一个RedisTemplate类,Redis底层的默认的
  8. RedisTemplate类就会失效!
  9. - 如-"图Ser2"

72f46e04c3d7fa6f46e81150d7463b46.png

图Ser1

f92c856d342987e99352a35533372baa.png

图Ser2

2. SpringBoot整合Redis实战

  1. ① 第一步:导入依赖
  2. <!--操作redis-->
  3. <dependency>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter-data-redis</artifactId>
  6. </dependency>
  7. ② 第二步:application.properties中配置Redis的连接
  8. # 配置redis
  9. spring.redis.host=127.0.0.1
  10. springboot.redis.port=6379
  11. ③ 第三步:创建一个User实体类并实现序列化
  12. - 让User实现序列化,防止使用Redis存储并传递User对象的时候,报序列化的错误,
  13. @Data
  14. @AllArgsConstructor
  15. @NoArgsConstructor
  16. @Component
  17. public class User implements Serializable {
  18. private String username;
  19. private int age;
  20. }
  21. ④ 第四步:由于User实现了序列化之后,User中的数据在传递的过程中需要进行解析,
  22. 才可使正常显示,Redis默认的序列化解析方式是:JDK序列化,对于默认的序列化方式
  23. 我们可以自定义一个自己的序列化方式来取代Redis默认的序列化方式"RedisConfig"
  24. package com.lzk.config;
  25. /**
  26. * Created by Linzk on 2020/11
  27. *
  28. * -->下面重写Redis的配置类,解决序列化乱码问题
  29. * -->编写自己的redisTemplate的序列化方式
  30. * -->下面的redisTemplate就是一个模板,以后关于Redis的序列化问题,
  31. * 此RedisConfig类,直接拿来使用即可!
  32. *
  33. */
  34. @Configuration //告诉Spring这是一个配置类
  35. public class RedisConfig {
  36. /**
  37. * 下面自定义了一个redisTemplate
  38. * -->分别创建了两种序列化解析方式,来替换运来的JDK序列化解析方式
  39. * 方式一:Json序列化方式
  40. * 方式二:String类型的序列化方式
  41. */
  42. @Bean
  43. public RedisTemplate<String, Object> redisTemplate1(RedisConnectionFactory factory)
  44. throws UnknownHostException {
  45. //我们为了开发方便,直接使用<String, Object>类型
  46. RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
  47. template.setConnectionFactory(factory);
  48. /**
  49. * 1. 方式一:创建Json的序列化方式
  50. * -->配置Redis的序列化解析方式是Json
  51. * -->即:通过Json去解析任何传进来的实体类对象,
  52. * 而不是使用原来的JDK去解析。
  53. */
  54. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  55. //1.1 Json的序列化方式,我们需要通过ObjectMapper进行转义
  56. ObjectMapper om = new ObjectMapper();
  57. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  58. om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  59. //1.2 转义完就可以使用Json的序列化方式了
  60. jackson2JsonRedisSerializer.setObjectMapper(om);
  61. /**
  62. * 2.方式二:创建String类型的序列化方式
  63. * -->配置Redis的序列化解析方式是Json
  64. * -->即:通过Json去解析任何传进来的实体类对象,
  65. * 而不是使用原来的JDK去解析。
  66. */
  67. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  68. /**
  69. * 3. 上面创建好两种序列化方式后,将两种序列化方式
  70. * 分别应用到(set)Redis的不同数据类型里面去使用
  71. *
  72. */
  73. // 所有String类型的key都采用String的序列化方式
  74. template.setKeySerializer(stringRedisSerializer);
  75. // 所有hash类型的key也采用String的序列化方式
  76. template.setHashKeySerializer(stringRedisSerializer);
  77. // 所有的value类型的序列化方式采用jackson
  78. template.setValueSerializer(jackson2JsonRedisSerializer);
  79. // 如果是hash的value序列化方式采用jackson
  80. template.setHashValueSerializer(jackson2JsonRedisSerializer);
  81. //上面设置好每一种类型对应的序列化方式后,将所有的properties设置set进去。
  82. template.afterPropertiesSet();
  83. return template;
  84. }
  85. }
  86. ⑤ 第五步:在企业开发中,要使用Redis操作各种类型,就将Redis里面各个指令的操作方法封装
  87. 到一个工具类中RedisUtil,直接调用这个RedisUtil工具类即可!
  88. RedisUtils工具类↓:
  89. package com.lzk.utils;
  90. /**
  91. * Created by Linzk on 2020/11
  92. *
  93. * 操作Redis的工具类
  94. */
  95. @Component
  96. public final class RedisUtil {
  97. //以下@Autowired导入的是我们自己写的RedisTemplate配置类
  98. @Autowired
  99. private RedisTemplate<String, Object> redisTemplate;
  100. // =============================common============================
  101. /**
  102. * 指定缓存失效时间
  103. * @param key 键
  104. * @param time 时间(秒)
  105. */
  106. public boolean expire(String key, long time) {
  107. try {
  108. if (time > 0) {
  109. redisTemplate.expire(key, time, TimeUnit.SECONDS);
  110. }
  111. return true;
  112. } catch (Exception e) {
  113. e.printStackTrace();
  114. return false;
  115. }
  116. }
  117. /**
  118. * 根据key 获取过期时间
  119. * @param key 键 不能为null
  120. * @return 时间(秒) 返回0代表为永久有效
  121. */
  122. public long getExpire(String key) {
  123. return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  124. }
  125. /**
  126. * 判断key是否存在
  127. * @param key 键
  128. * @return true 存在 false不存在
  129. */
  130. public boolean hasKey(String key) {
  131. try {
  132. return redisTemplate.hasKey(key);
  133. } catch (Exception e) {
  134. e.printStackTrace();
  135. return false;
  136. }
  137. }
  138. /**
  139. * 删除缓存
  140. * @param key 可以传一个值 或多个
  141. */
  142. @SuppressWarnings("unchecked")
  143. public void del(String... key) {
  144. if (key != null && key.length > 0) {
  145. if (key.length == 1) {
  146. redisTemplate.delete(key[0]);
  147. } else {
  148. redisTemplate.delete(CollectionUtils.arrayToList(key));
  149. }
  150. }
  151. }
  152. // ============================String=============================
  153. /**
  154. * 普通缓存获取
  155. * @param key 键
  156. * @return 值
  157. */
  158. public Object get(String key) {
  159. return key == null ? null : redisTemplate.opsForValue().get(key);
  160. }
  161. /**
  162. * 普通缓存放入
  163. * @param key 键
  164. * @param value 值
  165. * @return true成功 false失败
  166. */
  167. public boolean set(String key, Object value) {
  168. try {
  169. redisTemplate.opsForValue().set(key, value);
  170. return true;
  171. } catch (Exception e) {
  172. e.printStackTrace();
  173. return false;
  174. }
  175. }
  176. /**
  177. * 普通缓存放入并设置时间
  178. * @param key 键
  179. * @param value 值
  180. * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
  181. * @return true成功 false 失败
  182. */
  183. public boolean set(String key, Object value, long time) {
  184. try {
  185. if (time > 0) {
  186. redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
  187. } else {
  188. set(key, value);
  189. }
  190. return true;
  191. } catch (Exception e) {
  192. e.printStackTrace();
  193. return false;
  194. }
  195. }
  196. /**
  197. * 递增
  198. * @param key 键
  199. * @param delta 要增加几(大于0)
  200. */
  201. public long incr(String key, long delta) {
  202. if (delta < 0) {
  203. throw new RuntimeException("递增因子必须大于0");
  204. }
  205. return redisTemplate.opsForValue().increment(key, delta);
  206. }
  207. /**
  208. * 递减
  209. * @param key 键
  210. * @param delta 要减少几(小于0)
  211. */
  212. public long decr(String key, long delta) {
  213. if (delta < 0) {
  214. throw new RuntimeException("递减因子必须大于0");
  215. }
  216. return redisTemplate.opsForValue().increment(key, -delta);
  217. }
  218. // ================================Map=================================
  219. /**
  220. * HashGet
  221. * @param key 键 不能为null
  222. * @param item 项 不能为null
  223. */
  224. public Object hget(String key, String item) {
  225. return redisTemplate.opsForHash().get(key, item);
  226. }
  227. /**
  228. * 获取hashKey对应的所有键值
  229. * @param key 键
  230. * @return 对应的多个键值
  231. */
  232. public Map<Object, Object> hmget(String key) {
  233. return redisTemplate.opsForHash().entries(key);
  234. }
  235. /**
  236. * HashSet
  237. * @param key 键
  238. * @param map 对应多个键值
  239. */
  240. public boolean hmset(String key, Map<String, Object> map) {
  241. try {
  242. redisTemplate.opsForHash().putAll(key, map);
  243. return true;
  244. } catch (Exception e) {
  245. e.printStackTrace();
  246. return false;
  247. }
  248. }
  249. /**
  250. * HashSet 并设置时间
  251. * @param key 键
  252. * @param map 对应多个键值
  253. * @param time 时间(秒)
  254. * @return true成功 false失败
  255. */
  256. public boolean hmset(String key, Map<String, Object> map, long time) {
  257. try {
  258. redisTemplate.opsForHash().putAll(key, map);
  259. if (time > 0) {
  260. expire(key, time);
  261. }
  262. return true;
  263. } catch (Exception e) {
  264. e.printStackTrace();
  265. return false;
  266. }
  267. }
  268. /**
  269. * 向一张hash表中放入数据,如果不存在将创建
  270. *
  271. * @param key 键
  272. * @param item 项
  273. * @param value 值
  274. * @return true 成功 false失败
  275. */
  276. public boolean hset(String key, String item, Object value) {
  277. try {
  278. redisTemplate.opsForHash().put(key, item, value);
  279. return true;
  280. } catch (Exception e) {
  281. e.printStackTrace();
  282. return false;
  283. }
  284. }
  285. /**
  286. * 向一张hash表中放入数据,如果不存在将创建
  287. *
  288. * @param key 键
  289. * @param item 项
  290. * @param value 值
  291. * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
  292. * @return true 成功 false失败
  293. */
  294. public boolean hset(String key, String item, Object value, long time) {
  295. try {
  296. redisTemplate.opsForHash().put(key, item, value);
  297. if (time > 0) {
  298. expire(key, time);
  299. }
  300. return true;
  301. } catch (Exception e) {
  302. e.printStackTrace();
  303. return false;
  304. }
  305. }
  306. /**
  307. * 删除hash表中的值
  308. *
  309. * @param key 键 不能为null
  310. * @param item 项 可以使多个 不能为null
  311. */
  312. public void hdel(String key, Object... item) {
  313. redisTemplate.opsForHash().delete(key, item);
  314. }
  315. /**
  316. * 判断hash表中是否有该项的值
  317. *
  318. * @param key 键 不能为null
  319. * @param item 项 不能为null
  320. * @return true 存在 false不存在
  321. */
  322. public boolean hHasKey(String key, String item) {
  323. return redisTemplate.opsForHash().hasKey(key, item);
  324. }
  325. /**
  326. * hash递增 如果不存在,就会创建一个 并把新增后的值返回
  327. *
  328. * @param key 键
  329. * @param item 项
  330. * @param by 要增加几(大于0)
  331. */
  332. public double hincr(String key, String item, double by) {
  333. return redisTemplate.opsForHash().increment(key, item, by);
  334. }
  335. /**
  336. * hash递减
  337. *
  338. * @param key 键
  339. * @param item 项
  340. * @param by 要减少记(小于0)
  341. */
  342. public double hdecr(String key, String item, double by) {
  343. return redisTemplate.opsForHash().increment(key, item, -by);
  344. }
  345. // ============================set=============================
  346. /**
  347. * 根据key获取Set中的所有值
  348. * @param key 键
  349. */
  350. public Set<Object> sGet(String key) {
  351. try {
  352. return redisTemplate.opsForSet().members(key);
  353. } catch (Exception e) {
  354. e.printStackTrace();
  355. return null;
  356. }
  357. }
  358. /**
  359. * 根据value从一个set中查询,是否存在
  360. *
  361. * @param key 键
  362. * @param value 值
  363. * @return true 存在 false不存在
  364. */
  365. public boolean sHasKey(String key, Object value) {
  366. try {
  367. return redisTemplate.opsForSet().isMember(key, value);
  368. } catch (Exception e) {
  369. e.printStackTrace();
  370. return false;
  371. }
  372. }
  373. /**
  374. * 将数据放入set缓存
  375. *
  376. * @param key 键
  377. * @param values 值 可以是多个
  378. * @return 成功个数
  379. */
  380. public long sSet(String key, Object... values) {
  381. try {
  382. return redisTemplate.opsForSet().add(key, values);
  383. } catch (Exception e) {
  384. e.printStackTrace();
  385. return 0;
  386. }
  387. }
  388. /**
  389. * 将set数据放入缓存
  390. *
  391. * @param key 键
  392. * @param time 时间(秒)
  393. * @param values 值 可以是多个
  394. * @return 成功个数
  395. */
  396. public long sSetAndTime(String key, long time, Object... values) {
  397. try {
  398. Long count = redisTemplate.opsForSet().add(key, values);
  399. if (time > 0)
  400. expire(key, time);
  401. return count;
  402. } catch (Exception e) {
  403. e.printStackTrace();
  404. return 0;
  405. }
  406. }
  407. /**
  408. * 获取set缓存的长度
  409. *
  410. * @param key 键
  411. */
  412. public long sGetSetSize(String key) {
  413. try {
  414. return redisTemplate.opsForSet().size(key);
  415. } catch (Exception e) {
  416. e.printStackTrace();
  417. return 0;
  418. }
  419. }
  420. /**
  421. * 移除值为value的
  422. *
  423. * @param key 键
  424. * @param values 值 可以是多个
  425. * @return 移除的个数
  426. */
  427. public long setRemove(String key, Object... values) {
  428. try {
  429. Long count = redisTemplate.opsForSet().remove(key, values);
  430. return count;
  431. } catch (Exception e) {
  432. e.printStackTrace();
  433. return 0;
  434. }
  435. }
  436. // ===============================list=================================
  437. /**
  438. * 获取list缓存的内容
  439. *
  440. * @param key 键
  441. * @param start 开始
  442. * @param end 结束 0 到 -1代表所有值
  443. */
  444. public List<Object> lGet(String key, long start, long end) {
  445. try {
  446. return redisTemplate.opsForList().range(key, start, end);
  447. } catch (Exception e) {
  448. e.printStackTrace();
  449. return null;
  450. }
  451. }
  452. /**
  453. * 获取list缓存的长度
  454. *
  455. * @param key 键
  456. */
  457. public long lGetListSize(String key) {
  458. try {
  459. return redisTemplate.opsForList().size(key);
  460. } catch (Exception e) {
  461. e.printStackTrace();
  462. return 0;
  463. }
  464. }
  465. /**
  466. * 通过索引 获取list中的值
  467. *
  468. * @param key 键
  469. * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
  470. */
  471. public Object lGetIndex(String key, long index) {
  472. try {
  473. return redisTemplate.opsForList().index(key, index);
  474. } catch (Exception e) {
  475. e.printStackTrace();
  476. return null;
  477. }
  478. }
  479. /**
  480. * 将list放入缓存
  481. *
  482. * @param key 键
  483. * @param value 值
  484. */
  485. public boolean lSet(String key, Object value) {
  486. try {
  487. redisTemplate.opsForList().rightPush(key, value);
  488. return true;
  489. } catch (Exception e) {
  490. e.printStackTrace();
  491. return false;
  492. }
  493. }
  494. /**
  495. * 将list放入缓存
  496. * @param key 键
  497. * @param value 值
  498. * @param time 时间(秒)
  499. */
  500. public boolean lSet(String key, Object value, long time) {
  501. try {
  502. redisTemplate.opsForList().rightPush(key, value);
  503. if (time > 0)
  504. expire(key, time);
  505. return true;
  506. } catch (Exception e) {
  507. e.printStackTrace();
  508. return false;
  509. }
  510. }
  511. /**
  512. * 将list放入缓存
  513. *
  514. * @param key 键
  515. * @param value 值
  516. * @return
  517. */
  518. public boolean lSet(String key, List<Object> value) {
  519. try {
  520. redisTemplate.opsForList().rightPushAll(key, value);
  521. return true;
  522. } catch (Exception e) {
  523. e.printStackTrace();
  524. return false;
  525. }
  526. }
  527. /**
  528. * 将list放入缓存
  529. *
  530. * @param key 键
  531. * @param value 值
  532. * @param time 时间(秒)
  533. * @return
  534. */
  535. public boolean lSet(String key, List<Object> value, long time) {
  536. try {
  537. redisTemplate.opsForList().rightPushAll(key, value);
  538. if (time > 0)
  539. expire(key, time);
  540. return true;
  541. } catch (Exception e) {
  542. e.printStackTrace();
  543. return false;
  544. }
  545. }
  546. /**
  547. * 根据索引修改list中的某条数据
  548. *
  549. * @param key 键
  550. * @param index 索引
  551. * @param value 值
  552. * @return
  553. */
  554. public boolean lUpdateIndex(String key, long index, Object value) {
  555. try {
  556. redisTemplate.opsForList().set(key, index, value);
  557. return true;
  558. } catch (Exception e) {
  559. e.printStackTrace();
  560. return false;
  561. }
  562. }
  563. /**
  564. * 移除N个值为value
  565. *
  566. * @param key 键
  567. * @param count 移除多少个
  568. * @param value 值
  569. * @return 移除的个数
  570. */
  571. public long lRemove(String key, long count, Object value) {
  572. try {
  573. Long remove = redisTemplate.opsForList().remove(key, count, value);
  574. return remove;
  575. } catch (Exception e) {
  576. e.printStackTrace();
  577. return 0;
  578. }
  579. }
  580. }
  581. ⑥ 第六步:测试
  582. -注意一: 此时底层使用的序列化方式redisTemplate就是我们自定义的redisTemplate而不是redis
  583. 默认的那个RedisTemplate类。
  584. -注意二: 由于我们将Redis的增删改查方法封装进一个工具类中,此工具类RedisUtil
  585. 里面使用的就是我们自定义的RedisTemplate类,因此我们要实现Redis的增删改查的操作,
  586. 直接调用此工具类RedisUtil来实现即可。
  587. @SpringBootTest
  588. class SpringbootRedisApplicationTests {
  589. /**
  590. * 引入Redis的工具类
  591. */
  592. @Autowired
  593. private RedisUtil redisUtil;
  594. @Test
  595. public void test1() {
  596. /**
  597. * 1. 利用创建的User实体类来保存数据
  598. * 2. 真实开发一般都是使用json来传递对象
  599. *
  600. */
  601. //创建User对象并赋值
  602. User user = new User("linzk123",3);
  603. //将序列化后的对象存到Redis中
  604. redisUtil.set("user", user);
  605. //根据键user获取存到Redis的jsonUser值
  606. System.out.println(redisUtil.get("user"));
  607. }
  608. }
  609. ↑运行test1()程序测试,成功序列化并解析出"user"的数据。

**注意**:以上纯属个人学习笔记,整理分享出来,希望对大家有帮助!!

--- 对于有不同观点的大V欢迎评论指点,一起成长,一起进步!!---

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号