在SpringBoot中使用Redis数据库时,如果RedisSerializer使用JdkSerializationRedisSerializer(默认值),需要被序列化Class实现Serializable接口,而且Redis数据库中数据很不直观(下图为Redis中存储的User类),不利于程序的调试和使用。
代码如下(以User为例,需实现Serializable接口):
User user = new User();
user.setId("1");
user.setUsername("zs");
user.setPassword("123");
RedisUtils.set("key",user);
User valueRedis = (User)RedisUtils.get("key");
Redis中存储结果如下:
Redis中的key:\xac\xed\x00\x05t\x00\x03key
Redis中的value:\xac\xed\x00\x05sr\x00\x1ecom.carl.security.entity.User1\xa1\xfd\xa2\xa8\xddV\xce\xc0\x02\x00\x03L\x00\x02idt\x00\x12Ljava/lang/String;L\x00\x08passwordq\x00~\x00\x01L\x00\x08usernameq\x00~\x00\x01xpt\x00\x011t\x00\x03123t\x00\x02zs
目前常用的RedisSerializer包括:JdkSerializationRedisSerializer(默认值)、StringRedisSerializer、Jackson2JsonRedisSerializer/GenericJackson2JsonRedisSerializer、GenericFastJsonRedisSerializer(来自com.alibaba.fastjson.support.spring包)等,正如前言所说,JdkSerializationRedisSerializer面临各种问题,所以这里主要介绍Jackson2JsonRedisSerializer和GenericFastJsonRedisSerializer两种RedisSerializer的特点与不足。
提示:本文中对于key都采用StringRedisSerializer进行序列化。
代码如下:
@Component
public class RedisUtils {
private static RedisTemplate<String, Object> redisTemplate;
public static RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
@Autowired
private RedisUtils(RedisConnectionFactory redisConnectionFactory){
//为了方便,一般直接使用
redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 将对象序列化
ObjectMapper om=new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
// json序列化配置
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer<>(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(om);
//key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
//hash的key也采用String 的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
//value的序列化方式采用jackson的方式
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
//hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
}
public static boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
}
Redis中存储结果如下:
Redis中的key:key
Redis中的value:{"@class": “com.carl.security.entity.User”, “id”: “1”, “username”: “zs”, “password”: “123”}
其序列化和反序列化都可正常进行。
代码如下:
RedisUtils.set("key","value");
String valueRedis = (String) RedisUtils.get("key");
Redis中存储结果如下:
Redis中的key:key
Redis中的value:value
其序列化和反序列化都可正常进行。
代码如下:
User[] value = new User[]{user,user};
RedisUtils.set("key","value");
User[] valueRedis = (User[])RedisUtils.get("key");
Redis中存储结果如下:
Redis中的key:key
Redis中的value:
[
“[Lcom.carl.security.entity.User1;”,
[
{
“@class”: “com.carl.security.entity.User1”,
“id”: “1”,
“username”: “zs”,
“password”: “123”
},
{
“@class”: “com.carl.security.entity.User1”,
“id”: “1”,
“username”: “zs”,
“password”: “123”
}
]
]
其序列化和反序列化都可正常进行。
代码如下:
String[] value = new String[]{"a","b","c","d"};
RedisUtils.set("key", value);
String[] valueRedis = (String[])RedisUtils.get("key");
Redis中存储结果如下:
Redis中的key:key
Redis中的value:[“a”, “b”, “c”, “d”]
其序列化可正常进行,但是反序列化有问题,异常信息如下:
Caused by: com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id 'a' as a subtype of `java.lang.Object`: no such class found
at [Source: (byte[])"["a","b","c","d"]"; line: 1, column: 6]
综合上一节结论,可以发现:Jackson2JsonRedisSerializer对String数组进行序列化时会省略其类型的描述,直接进行存储,因此导致反序列化时认为“a”是一个类型参数,导致反序列化失败,该问题作者暂未发现解决方法。
代码如下:
@Component
public class RedisUtils {
private static RedisTemplate<String, Object> redisTemplate;
public static RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
@Autowired
private RedisUtils(RedisConnectionFactory redisConnectionFactory){
//为了方便,一般直接使用
redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// json序列化配置
GenericFastJsonRedisSerializer jackson2JsonRedisSerializer = new GenericFastJsonRedisSerializer();
//key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
//hash的key也采用String 的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
//value的序列化方式采用jackson的方式
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
//hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
}
public static boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
}
Redis中存储结果如下:
Redis中的key:key
Redis中的value:{"@type": “com.carl.security.entity.User”, “id”: “1”, “username”: “zs”, “password”: “123”}
其序列化和反序列化都可正常进行。
代码如下:
RedisUtils.set("key","value");
String valueRedis = (String) RedisUtils.get("key");
Redis中存储结果如下:
Redis中的key:key
Redis中的value:value
其序列化和反序列化都可正常进行。
代码如下:
User[] value = new User[]{user,user};
RedisUtils.set("key","value");
User[] valueRedis = (User[])RedisUtils.get("key");
Redis中存储结果如下:
Redis中的key:key
Redis中的value:
[
{
“@type”: “com.carl.security.entity.User”,
“id”: “1”,
“password”: “123”,
“username”: “zs”
},
{
“$ref”: “$[0]”
}
]
其序列化可正常进行,但是反序列化报异常,异常信息如下:
java.lang.ClassCastException: com.alibaba.fastjson.JSONArray cannot be cast to [Lcom.carl.security.entity.User;
从异常信息我们可以看出,GenericFastJsonRedisSerializer是将数组反序列化为JSONArray ,查看JSONArray类的定义就可以看出其是一种List集合,所以将其强转为User数组就会报错。
public class JSONArray extends JSON implements List<Object>, Cloneable, RandomAccess, Serializable {
......
}
修改反序列化后的强转类型,就没有异常信息了。
List<User> valueRedis = (List<User>)RedisUtils.get("key");
代码如下:
String[] value = new String[]{"a","b","c","d"};
RedisUtils.set("key", value);
List<String> valueRedis = (List<String>)RedisUtils.get("key");
Redis中存储结果如下:
Redis中的key:key
Redis中的value:[“a”, “b”, “c”, “d”]
其序列化和反序列化都可正常进行。
本文对几种常见的RedisSerializer进行对比分析,总结出Jackson2JsonRedisSerializer和GenericFastJsonRedisSerializer的技术特点和目前遇到的问题,结论如下: