一次生產事故引發的JDK序列化思考 1. 問題起因以及解決方案 2. 思考 推薦閱讀

起因:一次需求對一個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即可。

推薦閱讀

複習盤點-Java序列化方式(2)JAVA原生序列化以及Protostuff序列化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章