起因:一次需求對一個pojo類增加了新字段,並保存到了Redis中,測試沒有問題,但是到生產環境卻出現了反序列化失敗。
1. 問題起因以及解決方案
1. 失敗原因:
"Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException:
Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is
java.io.InvalidClassException: com.tellme.controller.RedisController$User; local class incompatible:
stream classdesc serialVersionUID = 3688184446225449117,
local class serialVersionUID = 6846178843376749314"
在複習盤點-Java序列化方式(2)JAVA原生序列化以及Protostuff序列化中,指出反序列化不同的UID導致InvalidClassException
異常。
原始代碼:
@RestController
public class RedisController {
@Autowired
private RedisTemplate redisTemplate;
//某一接口存儲到Redis中
@GetMapping("redis/set")
public String set() {
User user=new User();
user.setId(100);
user.setName("tom");
//保存到Redis中
redisTemplate.opsForValue().set("b:test",user);
User o = (User)redisTemplate.opsForValue().get("b:test");
return o.toString();
}
//別的接口在獲取Redis裏面存儲的對象
@GetMapping("redis/get")
public String get() {
User o = (User)redisTemplate.opsForValue().get("b:test");
return o.toString();
}
@Data
@ToString
public static class User implements Serializable {
private int id;
private String name;
}
}
注:Redis使用了默認的JDK的序列化方式和反序列化方式。
而一次新的需求中,User
對象加了一個字段,導致舊數據反序列化失敗。
根本原因:類serialVersionUID
是自動生成的,類被修改後,生成的serialVersionUID
就會變化。舊數據使用上一個版本的serialVersionUID
存儲到Redis中,而本地類的版本號爲新的serialVersionUID
。故不能反序列化。
那出現這個問題,解決方案是什麼呢?類需要手動聲明修改前的
serialVersionUID
。
@RestController
public class RedisController {
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("redis/set")
public String set() {
User user=new User();
user.setId(100);
user.setName("tom");
//保存到Redis中
redisTemplate.opsForValue().set("b:test",user);
User o = (User)redisTemplate.opsForValue().get("b:test");
return o.toString();
}
@GetMapping("redis/get")
public String get() {
User o = (User)redisTemplate.opsForValue().get("b:test");
return o.toString();
}
@Data
@ToString
public static class User implements Serializable {
//手動聲明爲舊版本的序列化號
private static final long serialVersionUID = 3688184446225449117L;
private int id;
private String name;
//新加的字段
private int age;
}
}
2. 思考
使用JDK序列化時,pojo類必須聲明Serializable
接口,pojo類中推薦手動聲明serialVersionUID,設置爲1L即可。