protostuff 避免 更改 java 对象字段 ,比如新增一个,导致 redis 等缓存 的数据反序列化失败问题??

问题重现:

我们有个方法 通过 attrKey 查询 List ,同时方法中有缓存,会优先查询缓存,没有读库,然后 写缓存 返回。

方法伪代码如下:

 public ConfAttr getConf(String attrKey) {// 从 缓存查询List<ConfAttr> attrValues = cacheClient.get(attrKey);if(attrValues !=null && !attrValues.isEmpty(){return attrValues.get(0);}// 读库List<ConfAttr> attrValues = selectFromDb(attrKey);cacheClient.put(attrKey,attrValues, cacheSeconds);return attrValues.get(0);
}

然后某天业务迭代 ConfAttr类增加 source 属性。上线后 redis 反序列化出错。错误内容如下:

Exception in thread "main" java.lang.RuntimeException: Reading from a byte array threw an IOException (should never happen).at io.protostuff.IOUtil.mergeFrom(IOUtil.java:54)at io.protostuff.ProtostuffIOUtil.mergeFrom(ProtostuffIOUtil.java:104)at com.cm.cache.serialize.ProtostuffSerializer.deserialize(ProtostuffSerializer.java:34)at com.cm.cache.redis.ShardedRedisClient.get(ShardedRedisClient.java:88)at com.test.RedisTest.main(RedisTest.java:51)
Caused by: io.protostuff.ProtobufException: CodedInput encountered an embedded string or bytes that misreported its size.at io.protostuff.ProtobufException.misreportedSize(ProtobufException.java:86)at io.protostuff.ByteArrayInput.readString(ByteArrayInput.java:438)at io.protostuff.runtime.RuntimeUnsafeFieldFactory$9$1.mergeFrom(RuntimeUnsafeFieldFactory.java:753)at io.protostuff.runtime.RuntimeSchema.mergeFrom(RuntimeSchema.java:466)at io.protostuff.runtime.ObjectSchema.readObjectFrom(ObjectSchema.java:693)at io.protostuff.runtime.IdStrategy$8.mergeFrom(IdStrategy.java:503)at io.protostuff.ByteArrayInput.mergeObjectEncodedAsGroup(ByteArrayInput.java:518)at io.protostuff.ByteArrayInput.mergeObject(ByteArrayInput.java:490)at io.protostuff.runtime.IdStrategy$10.mergeFrom(IdStrategy.java:583)at io.protostuff.runtime.IdStrategy$10.mergeFrom(IdStrategy.java:528)at io.protostuff.runtime.ObjectSchema.readObjectFrom(ObjectSchema.java:590)at io.protostuff.runtime.ObjectSchema.mergeFrom(ObjectSchema.java:350)at io.protostuff.ByteArrayInput.mergeObjectEncodedAsGroup(ByteArrayInput.java:518)at io.protostuff.ByteArrayInput.mergeObject(ByteArrayInput.java:490)at io.protostuff.runtime.RuntimeUnsafeFieldFactory$15$1.mergeFrom(RuntimeUnsafeFieldFactory.java:1217)at io.protostuff.runtime.RuntimeSchema.mergeFrom(RuntimeSchema.java:466)at io.protostuff.IOUtil.mergeFrom(IOUtil.java:45)... 4 more

问题答案

正确答案1:
可以使用@Tag 注解 指定字段顺序。

错误答案1:
将新增的字段加载java bean 类 的末尾 就可以避免该问题了(但是实际上这种还是存在错误的可能 ,具体参考 下一节的原理分析),

PS: 这次出问题 是因为 把sourceid 加入到 属性定义的中间了。

基于错误答案1的尝试截图

改动前:

改动后:

知识点拓展 protostuff 按照什么顺序来给类的 字段 序列化呢?

说明

  1. protostuff 只序列话字段值,不序列化 key(map可能除外)
  2. 顺序默认按照 typeClass.getDeclaredFields() (但是 jdk的这个方法 返回顺序,不是按照源码 的字段申明顺序,可能会被jdk 重编译 而改变顺序,大部分时候是按照申明的顺序)
  3. 所以 有时候添加字段,如果加载类 字段申明的末尾,不会出问题,加在中间,反序列化就会出问题。
  4. 不能依赖于 typeClass.getDeclaredFields(), 强制要求 按照 @Tag 添加指定字段顺序。(参考 https://houbb.github.io/2018/07/01/reflection-12-fields)
  5. protostuff 根据 getDeclaredFields 获取字段列表:会忽略 static transient 以及用注解@Exclude 。

方法入口

入口
io.protostuff.runtime.RuntimeSchema#fill

static void fill(Map<String, java.lang.reflect.Field> fieldMap,Class<?> typeClass)
{if (Object.class != typeClass.getSuperclass())fill(fieldMap, typeClass.getSuperclass());for (java.lang.reflect.Field f : typeClass.getDeclaredFields()){int mod = f.getModifiers();if (!Modifier.isStatic(mod) && !Modifier.isTransient(mod) && f.getAnnotation(Exclude.class) == null)fieldMap.put(f.getName(), f);}
}

调用图

004-protostuff踩坑-java bean新增字段反序列化失败问题相关推荐

  1. Java bean中字段命名潜规则,前两个字母要么都大写,要么都小写

    Java bean中字段命名潜规则,前两个字母要么都大写,要么都小写,否则会出错 以下代码是获取字段名的源码,根据这段代码可以得知: 输入         输出 AA             AA A ...

  2. #Jetson-NX踩坑记--Etcher Flash Failed 烧录失败的解决办法

    #Jetson-NX踩坑记--Etcher Flash Failed 烧录失败的解决办法 问题 解决方案 问题 根据 官方教程 使用 Etcher 烧录 Jetson NX 的镜像文件时总是失败,如图 ...

  3. Vue 踩坑笔记: 引入 ElementUI 时打包失败修复记录(ERROR in ./node_modules/element-ui/lib/theme-chalk/index.css)

    Vue 踩坑笔记: 引入 ElementUI 时打包失败修复记录(ERROR in ./node_modules/element-ui/lib/theme-chalk/index.css Module ...

  4. #踩坑 Java 使用itextpdf将PDF模板导出文件过大

    itextpdf导出文件 踩坑 问题产生 解决方案 最终方法 涉及到的公共方法/参数 问题产生 处理需求将PDF模板导出后发现前端调取文件时间过长,1min左右.查看才发现导出的文件6m左右,又查看了 ...

  5. java split 坑_java String split 踩坑记

    split操作是出镜率非常高的一个方法, 但是我们使用中通常会使用两个类提供的split方法, 他们在入参类型一样, 但是效果却有一些差别, 稍不注意容易踩坑. java.lang.String#sp ...

  6. mybatis学习与踩坑记录

    mybatis resultmap高级映射 应用场景:如果sql查询的列名和pojo的属性名不一致,可以使用resultMap将列名和pojo的属性名作一个对应关系,就可以映射成功了.(如果返回值为i ...

  7. 安装sql server踩坑记【sql2000程序安装配置服务器失败】

    安装sql server踩坑记 安装程序配置服务器失败.参考服务器错误日志和 C:\WINDOWS\sqlstp.log 了解更多信息. 在C:\Program Files\Microsoft SQL ...

  8. 测试踩坑 - 当已有接口(或数据库表中)新增字段时,都需要注意哪些测试点?

    最新在测试的时候,遇到了几个典型问题,都是在原有接口(或数据库表中)进行需求迭代时,出现的问题.我稍微总结一下,作为后续测试的经验教训. 问题一.数据库表中新增字段,但开发将这几个字段设置成了非空字段 ...

  9. 开发|MySQL新增字段踩坑

    提醒一下自己!!!! 在开发的过程中!!一些上线的项目,有一定人数日常使用的项目,不要在日常时段进行一些大数据量的表的新增字段(比如用户表)!!!!! 会卡死,只能重启数据库

最新文章

  1. 超人类AI的幻想与思考:自下而上构建的自我迭代意识系统
  2. 解析时代需要什么样的根目录,中国科技需要什么样的根技术
  3. freebsd 同步工具unison
  4. python---基础知识回顾(十)进程和线程(协程gevent:线程在I/O请求上的优化)...
  5. java的JDK配置
  6. python后端教程_Python学习教程(技术干货):关于前后端分离开发入门
  7. Create a virtualbox Based CentOS 6 OpenStack Cloud Image
  8. 转-----EasyCHM制作教程
  9. 矩阵乘法,输出结果矩阵
  10. 电路串联和并联图解_串联电路与并联电路的区别
  11. 小和尚打水问题_操作系统进程同步问题解析(哲学家问题、生产消费问题、小和尚打水问题等大量例子)...
  12. 为什么现在的程序员那么卑微?青出于蓝而胜于蓝啊
  13. Codeforces Problem-1591B Array Eversion
  14. 心得体会标题大全_给心得起个标题
  15. Vue开发之基础路由
  16. DDR扫盲——DDR与DDR2、DDR3的区别
  17. 亚马逊测评的获得方法及测评环境系统介绍,一次诊断全部解决。
  18. Cont. TF-IDF (BigData Data Mining)
  19. 爬虫系列(四)--全站爬取
  20. Xilinx FPGA PCIe XDMA性能测试报告(一)

热门文章

  1. 有出租高性能服务器的么,租用高性能的美国服务器有怎样配置呢?
  2. 服务器系统安装提示无法创建新的系统分区,安装win10系统提示“我们无法创建新的分区,也找不到现在的分区”如何解决...
  3. 机器学习--过度拟合 欠拟合
  4. jsqlparser mysql_使用JSQLParser解析SQL中涉及到的表
  5. 带你了解什么是Thymeleaf(实操)
  6. 从高中到大学 寻找真实的自己
  7. 休学的影响来了,家长多养孩子一年
  8. CE修改模拟器里的数据
  9. Java中的String,StringBuffer,StringBuilder有什么区别?
  10. Python学习笔记——用户登录测试