1. 前言
Redis是当前最流行的键值数据存储,是一个基于内存的高性能存储系统,常用于缓存、队列、消息中间件等领域。而SpringBoot是一个基于Spring框架的快速开发框架,可以帮助开发者快速搭建可独立运行的Spring应用程序。SpringBoot集成Redis可以帮助我们更加轻松地使用Redis,极大地提高了开发效率。但有时我们在使用Redis存对象时,会出现乱码的情况,这时就需要一些方法来解决这个问题。
2. 问题描述
在使用SpringBoot集成Redis存储对象时,可能会遇到一些问题。如下所示:
public class User implements Serializable {
private String username;
private String password;
// 省略getter和setter方法
}
@Autowired
private RedisTemplate redisTemplate;
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
redisTemplate.opsForValue().set("user", user);
User result = redisTemplate.opsForValue().get("user");
System.out.println(result.getUsername()); //输出的结果是乱码
当我们使用RedisTemplate存储对象时,有时会出现这样的问题:当我们从Redis中获取这个对象时,其属性值会变为乱码。如上述代码中,我们存入的用户名为"张三",但输出的却是乱码,这是非常不符合我们的预期的,并且非常影响我们使用Redis存储对象。
3. 问题原因
造成这个问题的原因是RedisTemplate的默认序列化器使用的是JdkSerializationRedisSerializer,这个序列化器将Java对象序列化后的结果写入Redis中,不同的操作系统、不同的JVM版本对序列化实现有所不同。因此,在存储和读取时会出现乱码的情况。
4. 解决方案
4.1 自定义序列化器
我们可以自己实现一个序列化器,使用自己的序列化策略,可以避免使用默认的序列化器而出现乱码的问题。首先,我们需要实现一个RedisSerializer接口,根据需要实现其序列化方法和反序列化方法:
public class ObjectRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
public ObjectRedisSerializer() {
this(Charset.forName("UTF8"));
}
public ObjectRedisSerializer(Charset charset) {
this.charset = charset;
}
@Override
public byte[] serialize(Object t) throws SerializationException {
try {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream);
objectOutputStream.writeObject(t);
objectOutputStream.flush();
objectOutputStream.close();
return byteStream.toByteArray();
} catch (IOException e) {
throw new SerializationException(e.getMessage(), e);
}
}
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length == 0) {
return null;
}
try {
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteStream);
return objectInputStream.readObject();
} catch (IOException e) {
throw new SerializationException(e.getMessage(), e);
} catch (ClassNotFoundException e) {
throw new SerializationException(e.getMessage(), e);
}
}
}
我们将Java对象序列化成二进制数据保存在Redis中,再将保存的二进制数据反序列化成对象。这样即使不同的操作系统、不同的JVM实现也可以正常序列化,避免了乱码的问题。使用自定义的序列化器:
RedisTemplate redisTemplate = new RedisTemplate<>();
RedisSerializer<String> stringSer = new StringRedisSerializer();
RedisSerializer<Object> objectSer = new ObjectRedisSerializer();
redisTemplate.setKeySerializer(stringSer);
redisTemplate.setValueSerializer(objectSer);
redisTemplate.setHashKeySerializer(stringSer);
redisTemplate.setHashValueSerializer(objectSer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
4.2 使用JSON序列化器
除了自定义序列化器,我们还可以使用更加简单的方式:使用Jackson序列化器,将对象转换成JSON字符串存储到Redis中,然后再将JSON字符串转换成Java对象。这种方式十分简单,而且Jackson序列化器是很常用的工具,别的地方也可以使用。
我们需要添加Jackson相关依赖:
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.3'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.12.3'
implementation 'com.fasterxml.jackson.module:jackson-module-parameter-names:2.12.3'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.3'
配置自定义的RedisTemplate:
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer(Charset.forName("UTF-8")));
redisTemplate.setHashKeySerializer(new StringRedisSerializer(Charset.forName("UTF-8")));
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
5. 总结
使用SpringBoot集成Redis存储对象时,乱码是一个比较常见的问题。我们可以通过自定义序列化器或使用JSON序列化器来避免这个问题。具体需要根据实际情况来选择合适的解决方案。