15 | 序列化:一来一回你还是原来的你吗?
该思维导图由 AI 生成,仅供参考
序列化和反序列化需要确保算法一致
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了序列化和反序列化在软件开发中的重要性和应用。首先介绍了序列化算法的选择和权衡性能、易用性和跨平台性等因素的重要性。其次重点讨论了在实际开发中常见的序列化问题,如Redis、参数和响应序列化反序列化,并提出了相应的解决方案。此外,还介绍了在使用RedisTemplate和StringRedisTemplate时的注意事项,以及如何自定义RedisTemplate的Key和Value的序列化方式。最后,通过具体案例分析了Jackson JSON反序列化对额外字段的处理和反序列化时要小心类的构造方法的问题,并提供了相应的解决方案。整体而言,本文内容涵盖了序列化和反序列化的选择、常见问题以及在Redis中的应用,为读者在实际开发中避免常见的序列化坑提供了指导。文章深入浅出,适合开发人员快速了解序列化和反序列化的相关知识。
《Java 业务开发常见错误 100 例》,新⼈⾸单¥59
全部留言(19)
- 最新
- 精选
- 梦倚栏杆老师,现在像fastJson, jackson 一般使用序列化和反序列化不都是属性类型兼容就能来回序列化吗?java序列化的时候存储序列化id记录版本号的意义是什么。 java序列化一开始存在的意义是什么?为什么要那样处理呢?如果按照现在fastJson 和jackson等的处理方式,toString 不也是一种序列化方式吗?反序列化时按照一种规则解析回去不就行了
作者回复: 1、有关serialVersionUID的意义: The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named serialVersionUID that must be static, final, and of type long: ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class serialVersionUID fields are not useful as inherited members. 2、toString也可以认为是一种文本序列化,序列化当然还可以按照自己的方式来做,只要是一致的方式实现对象到字节的转换。 3、 java序列化一开始存在的意义是什么?在有xml、json、protobuf等之前,jdk总需要有序列化来实现对象的文件存储、跨服务传输吧,当时确实互联网也没这么发达没考虑到异构体系的交互问题,我们不能以现在的眼光来看当时的技术为什么考虑这么不全面这么鸡肋
2020-04-1425 - Darren试着回答下今天的问题: 1、Long序列化的时候,Redis会认为是int,因此是获取不到的Long数据的,需要处理; 2、Jackson2ObjectMapperBuilder的采用了构建者模式创建对象;调用的是build()方法 public <T extends ObjectMapper> T build() { ObjectMapper mapper; if (this.createXmlMapper) { mapper = (this.defaultUseWrapper != null ? new XmlObjectMapperInitializer().create(this.defaultUseWrapper) : new XmlObjectMapperInitializer().create()); } else { mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper()); } configure(mapper); return (T) mapper; } 然后configure里面出了了甚多事情,比如:日式、Local、时间等的格式化器以及自定义属性命名策略等,具体详见https://github.com/y645194203/geektime-java-100/blob/master/ConfigInfo.java
作者回复: 比较坑的是,在Integer区间内返回的是Integer,超过这个区间返回Long @GetMapping("wrong2") public void wrong2() { String key = "testCounter"; countRedisTemplate.opsForValue().set(key, 1L); log.info("{} {}", countRedisTemplate.opsForValue().get(key), countRedisTemplate.opsForValue().get(key) instanceof Long); Long l1 = getLongFromRedis(key); countRedisTemplate.opsForValue().set(key, Integer.MAX_VALUE + 1L); log.info("{} {}", countRedisTemplate.opsForValue().get(key), countRedisTemplate.opsForValue().get(key) instanceof Long); Long l2 = getLongFromRedis(key); log.info("{} {} {}", l1, l2); } private Long getLongFromRedis(String key) { Object o = countRedisTemplate.opsForValue().get(key); if (o instanceof Integer) { return ((Integer) o).longValue(); } if (o instanceof Long) { return (Long) o; } return null; } 输出: 1 false 2147483648 true 1 2147483648
2020-04-14322 - 左琪老师,我之前遇到一个,我用redis存入一个Map<Long,object>,取出时发现却是Map<int,object>,然后响应给前端springmvc就报类型转换异常了,我redis的value也是用的Jackson序列化,自定义了objectmapper,正常对象都能序列化,反序列化,就是Long不行,我想知道该如何修正呢
作者回复: 这个long和int的问题应该就是我思考题的问题,你可以看看其他网友的回复以及我的回复如何解决。 我不知道你这里的用redis存入Map<Long,object>是不是指key是Long,value是Object,如果是的话,把数字作为Key不是一个好的实践,Redis的Key需要是字符串,并且区分命名空间,比如应用_领域_标识(或是数据库_表_PK),e.g.commonmistakes_redisexample_user123
2020-04-1610 - 👽之前其实一直还是比较喜欢枚举的,一直只是觉得枚举这个是个好的功能,只是我不会用。 现在来看,看来枚举在使用上确实时需要谨慎。 个人理解,枚举的本质,其实就是一个Map<Object,Object>,但是扩展性更强一些。其本身的存在,类似于一个 不可变的常量Map,本身的存在与意义,个人感觉,与数据字典也很像。存索引值(key),但对应一个具体对象或数值(value)。经过这一讲,之后的业务,我个人可能也会使用数据字典,而慎用枚举了。
作者回复: 内部没关系,也推荐使用枚举,对外是要慎用
2020-04-147 - 扎紧绷带有个同学说不用 int 来枚举,而选择语义性的字符串。我也觉得语义明确的字符串更好一些,但很多人认为数字占空间小,应该用int。老师怎么看,你们是怎么用的呢?
作者回复: 字符串略好点,空间方面其实不差这些
2020-07-046 - pedro关于枚举,无论是在 dto 还是数据库存储,我们都已经不用 int 来枚举了,而选择语义性的字符串,这在 debug 和维护上十分方便,也有利于迁移,int 枚举太难看了,每次调试,眼睛都花了😭
作者回复: 😀
2020-04-1425 - Michael我们项目中遇到的坑是:key是字符串,value是一个自定义对象,我们环境分为inner和prd,inner验证过了才会发生产,但是inner和prd是同一个Redis缓存和DB,value值对应的对象中加了字段,生产和inner同时作用,prd缓存失效了,正好把inner的给存进去了,结果导致生产接口从缓存取数据的时候出现反序列化报错问题,影响了生产。 后面采取方法是在缓存key加上环境前缀来避免这个问题。
作者回复: 对内环境和生产环境的问题也是比较典型的。对内虽然和生产公用数据库和中间件,但是毕竟还是用于测试验证的。 之前遇到的一个坑是类似的,对内环境和生产公用CDN,对内验证的时候传了一张测试图片上去,然后CDN节点就有了这个文件,到了生产上虽然又传了正式的图片(文件名没有变),但是CDN节点上的测试图片依旧缓存着,部分地区用户还是看到了测试图片。。。
2020-04-1922 - 💢 星星💢老师,我最近遇到一个坑,也是Jackson 序列化反序列的。一开始在xml中定义只定义mappingJacksonHttpMessageConverter,然后在DTO中某日期字段中加上@JsonDeserialize(using = DateJsonDeserialize.class)能实现日期转为功能。但是前台页面中显示的日期全都为时间戳。 于是在定义了ObjectMapper 里面重写了日期格式化的序列化方法,然后原先的进行接口Json格式反序列,@JsonDeserialize这个功能不能用了,找了好久都没有解决办法。老师这个坑,该如何解决?
作者回复: 最好帖下代码以及测试用例,否则很难看懂
2020-07-071 - 小杰log.info("longRedisTemplate get {}", (Long)longRedisTemplate.opsForValue().get("longRedisTemplate")); java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long 强转异常,也就是说我们获取到这样的值还要自己从Integer转成Long是吗老师?
作者回复: 是
2020-04-141 - 捞鱼的搬砖奇ObjectMapper 的 activateDefaultTyping 方法 在2.10版本才提供,那之前的版本有替代方案吗。
作者回复: 之前是enableDefaultTyping吧
2020-08-21