资讯专栏INFORMATION COLUMN

Spring-RedisTemplate写入数据乱码问题的复现与解决

李世赞 / 2729人阅读

摘要:是框架对的默认集成,我们在实际项目中,也经常使用它的去操作,一般来说没什么问题,但是细心一点的同学会发现,经过这种方法写入的数据会出现乱码问题问题复现项目依赖配置文件配置配置类注入设置数据存入的序列化方式实例化对象可以

</>复制代码

  1. org.springframework.data.redisSpring框架对Redis的默认集成,我们在实际项目中,也经常使用它的RedisTemplate去操作Redis,一般来说没什么问题,但是细心一点的同学会发现,经过这种方法写入redis的数据会出现乱码问题
问题复现 项目依赖

</>复制代码

  1. org.springframework.boot
  2. spring-boot-starter-web
  3. org.springframework.boot
  4. spring-boot-starter-test
  5. test
  6. org.springframework.boot
  7. spring-boot-starter-data-redis
Redis配置

yaml文件配置

</>复制代码

  1. spring:
  2. application:
  3. name: booklet-redis
  4. redis:
  5. host: 127.0.0.1
  6. port: 6379
  7. password: adminadmin
  8. timeout: 5000ms

Redis配置类

</>复制代码

  1. package com.liumapp.booklet.redis.config;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.redis.connection.RedisConnectionFactory;
  6. import org.springframework.data.redis.core.*;
  7. import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
  8. import org.springframework.data.redis.serializer.StringRedisSerializer;
  9. @Configuration
  10. public class RedisConfig {
  11. /**
  12. * 注入 RedisConnectionFactory
  13. */
  14. @Autowired
  15. RedisConnectionFactory redisConnectionFactory;
  16. @Bean
  17. public RedisTemplate functionDomainRedisTemplate() {
  18. RedisTemplate redisTemplate = new RedisTemplate<>();
  19. initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
  20. return redisTemplate;
  21. }
  22. /**
  23. * 设置数据存入 redis 的序列化方式
  24. *
  25. * @param redisTemplate
  26. * @param factory
  27. */
  28. private void initDomainRedisTemplate(RedisTemplate redisTemplate, RedisConnectionFactory factory) {
  29. redisTemplate.setKeySerializer(new StringRedisSerializer());
  30. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  31. redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
  32. redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
  33. redisTemplate.setConnectionFactory(factory);
  34. }
  35. /**
  36. * 实例化 HashOperations 对象,可以使用 Hash 类型操作
  37. *
  38. * @param redisTemplate
  39. * @return
  40. */
  41. @Bean
  42. public HashOperations hashOperations(RedisTemplate redisTemplate) {
  43. return redisTemplate.opsForHash();
  44. }
  45. /**
  46. * 实例化 ValueOperations 对象,可以使用 String 操作
  47. *
  48. * @param redisTemplate
  49. * @return
  50. */
  51. @Bean
  52. public ValueOperations valueOperations(RedisTemplate redisTemplate) {
  53. return redisTemplate.opsForValue();
  54. }
  55. /**
  56. * 实例化 ListOperations 对象,可以使用 List 操作
  57. *
  58. * @param redisTemplate
  59. * @return
  60. */
  61. @Bean
  62. public ListOperations listOperations(RedisTemplate redisTemplate) {
  63. return redisTemplate.opsForList();
  64. }
  65. /**
  66. * 实例化 SetOperations 对象,可以使用 Set 操作
  67. *
  68. * @param redisTemplate
  69. * @return
  70. */
  71. @Bean
  72. public SetOperations setOperations(RedisTemplate redisTemplate) {
  73. return redisTemplate.opsForSet();
  74. }
  75. /**
  76. * 实例化 ZSetOperations 对象,可以使用 ZSet 操作
  77. *
  78. * @param redisTemplate
  79. * @return
  80. */
  81. @Bean
  82. public ZSetOperations zSetOperations(RedisTemplate redisTemplate) {
  83. return redisTemplate.opsForZSet();
  84. }
  85. }
测试代码

</>复制代码

  1. package com.liumapp.booklet.redis;
  2. import org.junit.Test;
  3. import org.junit.runner.RunWith;
  4. import org.springframework.boot.test.context.SpringBootTest;
  5. import org.springframework.data.redis.core.ListOperations;
  6. import org.springframework.test.context.junit4.SpringRunner;
  7. import javax.annotation.Resource;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. @SpringBootTest(classes = BookletRedisMain.class)
  11. @RunWith(SpringRunner.class)
  12. public class BookletRedisMainTest {
  13. @Resource
  14. private ListOperations listOperations;
  15. @Test
  16. public void leftPushTest () {
  17. List list = new ArrayList<>();
  18. list.add("hello world");
  19. listOperations.leftPush("listKey", list);
  20. }
  21. }

运行上述测试代码后,我们会在redis中插入一组list类型的数据,其key为listKey,value为只有一个元素的list对象

接下来我们通过redis-cli去获取listKey这个值,可以看到乱码的出现:

</>复制代码

  1. 127.0.0.1:6379> LRANGE listKey 0 10
  2. 1) "xacxedx00x05srx00x13java.util.ArrayListxx81xd2x1dx99xc7ax9dx03x00x01Ix00x04sizexpx00x00x00x01wx04x00x00x00x01tx00x0bhello worldx"

当然,这对于我们项目的实际使用没有什么影响,在程序中再次获取listKey也不会出现乱码,只有通过redis-cli等工具直接取值的时候,才会出现乱码

问题出现原因

问题原因在于我们对Redis进行配置的这一段代码(事实上这也是redisTemplate的默认配置代码):

</>复制代码

  1. private void initDomainRedisTemplate(RedisTemplate redisTemplate, RedisConnectionFactory factory) {
  2. redisTemplate.setKeySerializer(new StringRedisSerializer());
  3. redisTemplate.setHashKeySerializer(new StringRedisSerializer());
  4. redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
  5. redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
  6. redisTemplate.setConnectionFactory(factory);
  7. }

在这里,redisTemplate对HashValue和Value的序列化类采用的是JDK默认的序列化策略,而不是String类型的序列化策略,所以我们在redis-cli中看到的value会因为序列化策略的问题,出现乱码

解决办法

将JDK默认的序列化策略更换为String类型的序列化策略

</>复制代码

  1. redisTemplate.setHashValueSerializer(new StringRedisSerializer());
  2. redisTemplate.setValueSerializer(new StringRedisSerializer());

但是这样做的话,我们在进行存储的时候,也只能存储String类型的数据,所以测试代码要进行如下修改

</>复制代码

  1. @Test
  2. public void leftPushTest () {
  3. List list = new ArrayList<>();
  4. list.add("hello world2");
  5. listOperations.leftPush("listKey", list.toString());
  6. }

再一次去redis-cli中取值,得到如下结果:

</>复制代码

  1. 127.0.0.1:6379> LRANGE listKey 0 10
  2. 1) "[hello world2]"
  3. 2) "xacxedx00x05srx00x13java.util.ArrayListxx81xd2x1dx99xc7ax9dx03x00x01Ix00x04sizexpx00x00x00x01wx04x00x00x00x01tx00x0bhello worldx"

可以发现乱码问题已经解决

总结

不建议更换redisTemplate默认的序列化策略,有乱码就让它乱着吧,反正知道正确的解码策略就不会影响程序的正常运行(不过通过php等其他语言去获取redis的值貌似不太好解决)

如果一定要更换策略,那么前往要注意,存储数据的类型要根据所选择的序列化策略去进行切换

项目案例源代码:github/booklet-redis

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75877.html

相关文章

  • Python 爬虫数据写入csv文件中文乱码解决以及天眼查爬虫数据写入csv

    摘要:爬虫数据写入文件中文乱码,用在中打开文件没有问题,但是用打开却出现了问题,以下为解决方法。 python爬虫数据写入csv文件中文乱码,用’utf-8‘在pycharm中打开文件没有问题,但是用excel打开却出现了问题,以下为解决方法。 (最近在练习爬虫,这个博文是对自己学习的记录和分享,...

    zone 评论0 收藏0

发表评论

0条评论

李世赞

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<