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

004-protostuff踩坑-java bean新增字段反序列化失败问题protostuff避免更改java对象字段,比如新增一个,导致redis等缓存的数据反序列化失败问题??问题重现:我们有个方法通过attrKey查询List,同时方法中有缓存,会优先查询缓存,没有读库,然后写缓存返回。方法伪代码如下:publicvoidsetId(StringattrKey){//从缓存查询List<ConfAttr>attrValues=cacheClient.get(attrKey);

大家好,又见面了,我是你们的朋友全栈君。

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的尝试截图

改动前:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Putj03Ba-1627548803636)(../../images/049c9aada59c718ec5457109bf13e52743ddb00da5efb01a81dc7c3fd317f060.png)]

改动后:

在这里插入图片描述

知识点拓展 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);
    }
}

调用图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXSrKAzV-1627548803639)(../../images/a489204dd0545cf88c9bf8c4f2b996f4fd40a36276197174d4a00d1af0d9b201.png)]

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139827.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • phpstorm2021.12 激活码-激活码分享

    (phpstorm2021.12 激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~41MD…

    2022年3月30日
    75
  • 二进制减法图解_二进制加法的算法图解

    二进制减法图解_二进制加法的算法图解二进制减法类似于十进制的减法,我们从十进制的减法来推出二进制减法如何进行运算。十进制减法例如74323-47562=26761的运算。灰色部分为计算过程,绿色字为被减一得到的数,红色字为借一后得到的数。在运算过程中,从右往左逐位进行计算。3-2=1;2不够减6,向前借一后加10变成12;12-6=6;3在上一步被借一所以减为2,2不够减5,…

    2022年9月25日
    3
  • MATLAB R2019b超详细安装教程(附完整安装文件)

    MATLAB R2019b超详细安装教程(附完整安装文件)摘要:本文详细介绍MATLABR2019b的安装步骤,为方便安装这里提供了完整安装文件的百度网盘下载链接供大家使用。从文件下载到证书安装本文都给出了每个步骤的截图,按照图示进行即可轻松完成安装使用。本文目录包括:下载安装包、正式安装步骤、复制替换文件、创建快捷方式。

    2022年6月11日
    230
  • FAST_ICA MTALAB工具包下载/ICA分析/独立成分分析MATLAB安装包/ICA toolbox

    FAST_ICA MTALAB工具包下载/ICA分析/独立成分分析MATLAB安装包/ICA toolbox很多小伙伴在后台问我,MATLAB怎么进行独立成分分析(ICA)。一般来讲,ICA操作可以说是EEG里面十分总要的操作。EEGLAB这方面做的非常好,只需要RunICA就能很快的进行EEG的分析,但同样也有其弊端(懂得都懂)。这里,我提供了matlab中FAST_ICA的安装包,由于年代较较远,因此,支持的MATLAB版本可能比较老。而且网址必须外网连接,如果有直接想要安装包的小伙伴可直接关注我的公众号,回复FAST_ICA,便可免费领取。打个小广告,粉爷公众号大厂面经,刷题指南,脑…

    2022年5月13日
    40
  • 快速上手Linux玩转典型应用

    快速上手Linux玩转典型应用

    2022年2月18日
    38
  • 十大排序算法小结

    十大排序算法小结

    2021年10月5日
    37

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号