org.springframework.data.redis是Spring框架对Redis的默认集成,我们在实际项目中,也经常使用它的RedisTemplate去操作Redis,一般来说没什么问题,但是细心一点的同学会发现,经过这种方法写入redis的数据会出现乱码问题
问题复现
项目依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
|
Redis配置
1 2 3 4 5 6 7 8
| spring: application: name: booklet-redis redis: host: 127.0.0.1 port: 6379 password: adminadmin timeout: 5000ms
|
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
| package com.liumapp.booklet.redis.config;
import org.springframework.beans.factory.annotation.Autowired; 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.*; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration public class RedisConfig {
@Autowired RedisConnectionFactory redisConnectionFactory;
@Bean public RedisTemplate<String,Object> functionDomainRedisTemplate() { RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); initDomainRedisTemplate(redisTemplate, redisConnectionFactory); return redisTemplate; }
private void initDomainRedisTemplate(RedisTemplate<String,Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(factory); }
@Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); }
@Bean public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForValue(); }
@Bean public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForList();
}
@Bean public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForSet(); }
@Bean public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForZSet(); } }
|
测试代码
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
| package com.liumapp.booklet.redis;
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.ListOperations; import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource; import java.util.ArrayList; import java.util.List;
@SpringBootTest(classes = BookletRedisMain.class) @RunWith(SpringRunner.class) public class BookletRedisMainTest {
@Resource private ListOperations<String, Object> listOperations;
@Test public void leftPushTest () { List<String> list = new ArrayList<>(); list.add("hello world"); listOperations.leftPush("listKey", list); }
}
|
运行上述测试代码后,我们会在redis中插入一组list类型的数据,其key为listKey,value为只有一个元素的list对象
接下来我们通过redis-cli去获取listKey这个值,可以看到乱码的出现:
1 2
| 127.0.0.1:6379> LRANGE listKey 0 10 1) "\xac\xed\x00\x05sr\x00\x13java.util.ArrayListx\x81\xd2\x1d\x99\xc7a\x9d\x03\x00\x01I\x00\x04sizexp\x00\x00\x00\x01w\x04\x00\x00\x00\x01t\x00\x0bhello worldx"
|
当然,这对于我们项目的实际使用没有什么影响,在程序中再次获取listKey也不会出现乱码,只有通过redis-cli等工具直接取值的时候,才会出现乱码
问题出现原因
问题原因在于我们对Redis进行配置的这一段代码(事实上这也是redisTemplate的默认配置代码):
1 2 3 4 5 6 7
| private void initDomainRedisTemplate(RedisTemplate<String,Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(factory); }
|
在这里,redisTemplate对HashValue和Value的序列化类采用的是JDK默认的序列化策略,而不是String类型的序列化策略,所以我们在redis-cli中看到的value会因为序列化策略的问题,出现乱码
解决办法
将JDK默认的序列化策略更换为String类型的序列化策略
1 2
| redisTemplate.setHashValueSerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer());
|
但是这样做的话,我们在进行存储的时候,也只能存储String类型的数据,所以测试代码要进行如下修改
1 2 3 4 5 6 7
| @Test public void leftPushTest () { List<String> list = new ArrayList<>(); list.add("hello world2");
listOperations.leftPush("listKey", list.toString()); }
|
再一次去redis-cli中取值,得到如下结果:
1 2 3
| 127.0.0.1:6379> LRANGE listKey 0 10 1) "[hello world2]" 2) "\xac\xed\x00\x05sr\x00\x13java.util.ArrayListx\x81\xd2\x1d\x99\xc7a\x9d\x03\x00\x01I\x00\x04sizexp\x00\x00\x00\x01w\x04\x00\x00\x00\x01t\x00\x0bhello worldx"
|
可以发现乱码问题已经解决
总结
不建议更换redisTemplate默认的序列化策略,有乱码就让它乱着吧,反正知道正确的解码策略就不会影响程序的正常运行(不过通过php等其他语言去获取redis的值貌似不太好解决)
如果一定要更换策略,那么前往要注意,存储数据的类型要根据所选择的序列化策略去进行切换
项目案例源代码:github/booklet-redis