Redis 序列化和反序列化
Redis Serialization and Deserialization
我注意到我存储在 Redis 中的一些序列化对象在反序列化时出现问题。
这通常发生在我更改存储在 Redis 中的对象 class 时。
我想了解问题,以便对解决方案有清晰的设计。
我的问题是,是什么导致了反序列化问题?
删除 public/private 属性 会导致问题吗?
也许添加新属性?
向 class 添加新函数会产生问题吗?更多构造函数如何?
在我的序列化对象中,我有一个属性 Map,如果我更改(更新一些属性,添加功能等)myObject,会导致反序列化问题吗?
什么原因导致反序列化问题?
在回答你的问题之前,我想给你一些背景知识,
序列化运行时与每个可序列化的 class 关联一个版本号,称为 serialVersionUID,在反序列化期间使用它来验证序列化对象的发送方和接收方是否已加载 classes与序列化兼容的对象。如果接收方为对象加载了 class,而该对象的 serialVersionUID 与相应发送方的 class 不同,则反序列化将导致 InvalidClassException。
如果可序列化 class 没有显式声明 serialVersionUID,那么序列化运行时将根据 class 的各个方面计算该 class 的默认 serialVersionUID 值,它使用 class 的以下信息来计算 SerialVersionUID,
- class 姓名。
- class 修饰符写成 32 位整数。
- 每个接口的名称按名称排序。
- 对于 class 的每个字段按字段名称排序(私有静态和私有瞬态字段除外:
- 字段名称。
- 写成 32 位整数的字段修饰符。
- 字段的描述符。
如果存在 class 初始值设定项,写出以下内容:
方法名称,.
方法的修饰符,java.lang.reflect.Modifier.STATIC,写成32位整数。
方法的描述符,()V。
对于每个按方法名称和签名排序的非私有构造函数:
方法名称,.
方法的修饰符写成 32 位整数。
方法的描述符。
对于按方法名称和签名排序的每个非私有方法:
方法的名称。
方法的修饰符写成 32 位整数。
方法的描述符。
所以,为了回答你的问题,
删除 public/private 属性 会导致问题吗?也许添加新属性?向 class 添加新函数会产生问题吗?更多构造函数如何?
是的,所有这些additions/removal默认都会导致问题。
但是克服这个问题的一种方法是显式定义 SerialVersionUID,这将告诉序列化系统我知道 class 会随着时间的推移而进化(或进化)并且不会抛出错误。所以反序列化系统只读取双方都存在的那些字段并赋值。反序列化端新添加的字段会得到默认值。如果在反序列化端删除了一些字段,算法只是读取并跳过。
以下是声明SerialVersionUID的方式,
private static final long serialVersionUID = 3487495895819393L;
我注意到我存储在 Redis 中的一些序列化对象在反序列化时出现问题。
这通常发生在我更改存储在 Redis 中的对象 class 时。
我想了解问题,以便对解决方案有清晰的设计。
我的问题是,是什么导致了反序列化问题? 删除 public/private 属性 会导致问题吗? 也许添加新属性? 向 class 添加新函数会产生问题吗?更多构造函数如何?
在我的序列化对象中,我有一个属性 Map,如果我更改(更新一些属性,添加功能等)myObject,会导致反序列化问题吗?
什么原因导致反序列化问题?
在回答你的问题之前,我想给你一些背景知识,
序列化运行时与每个可序列化的 class 关联一个版本号,称为 serialVersionUID,在反序列化期间使用它来验证序列化对象的发送方和接收方是否已加载 classes与序列化兼容的对象。如果接收方为对象加载了 class,而该对象的 serialVersionUID 与相应发送方的 class 不同,则反序列化将导致 InvalidClassException。
如果可序列化 class 没有显式声明 serialVersionUID,那么序列化运行时将根据 class 的各个方面计算该 class 的默认 serialVersionUID 值,它使用 class 的以下信息来计算 SerialVersionUID,
- class 姓名。
- class 修饰符写成 32 位整数。
- 每个接口的名称按名称排序。
- 对于 class 的每个字段按字段名称排序(私有静态和私有瞬态字段除外:
- 字段名称。
- 写成 32 位整数的字段修饰符。
- 字段的描述符。
如果存在 class 初始值设定项,写出以下内容:
方法名称,.
方法的修饰符,java.lang.reflect.Modifier.STATIC,写成32位整数。
方法的描述符,()V。
对于每个按方法名称和签名排序的非私有构造函数:
方法名称,.
方法的修饰符写成 32 位整数。
方法的描述符。
对于按方法名称和签名排序的每个非私有方法:
方法的名称。
方法的修饰符写成 32 位整数。
方法的描述符。
所以,为了回答你的问题,
删除 public/private 属性 会导致问题吗?也许添加新属性?向 class 添加新函数会产生问题吗?更多构造函数如何?
是的,所有这些additions/removal默认都会导致问题。
但是克服这个问题的一种方法是显式定义 SerialVersionUID,这将告诉序列化系统我知道 class 会随着时间的推移而进化(或进化)并且不会抛出错误。所以反序列化系统只读取双方都存在的那些字段并赋值。反序列化端新添加的字段会得到默认值。如果在反序列化端删除了一些字段,算法只是读取并跳过。
以下是声明SerialVersionUID的方式,
private static final long serialVersionUID = 3487495895819393L;