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)]

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(1)


相关推荐

  • mysql的uuid获取「建议收藏」

    mysql的uuid获取「建议收藏」mysql>SELECTUUID();mysql>c2cb8f66-351f-11e7-b3ed-00163e0429b6mysql>SELECTREPLACE(UUID(),’-‘,”);#将’-‘符号替换掉mysql>45c87fa0352211e78d40d4977a9ea871带‘-’字段长度是36,去掉后32位…

  • 廖雪峰git学习资料-涂改笔记

    廖雪峰git学习资料-涂改笔记注意:本文章是看廖雪峰官网资料整理而来原地址如下:http://www.liaoxuefeng.com/附件为git常用命令 前言:注意的问题如果是首次提交会第一步:先在本地建立一个一样的仓库,称本地仓库。 第二步:在本地进行commit操作将把更新提交到本地仓库; 第三步:将服务器端的更新pull到本地仓库进行合并,最后将合并好的本地仓库push到服务…

  • python数组求和_python数组求和

    python数组求和_python数组求和广告关闭腾讯云11.11云上盛惠,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!作者:dyq666,zhihu.compeopledyq666本专题主要介绍哈希表和指针两种方法来解决该类问题,从两个数之和引申到三个数之和,再从四个数之和的问题上思考如何构建出一种通用的代码(可以解决n个数之和)。本文主要内容是通过001问题来初步了解数组求和的两种常用方法。0…

  • pycharm中如何取消多行注释_pycharm怎么注释代码

    pycharm中如何取消多行注释_pycharm怎么注释代码pycharm中同时注释(取消注释)多行(单行)代码的快捷键都是Ctrl+/:选中代码,同时按住Ctrl+/(command+/),被选中行被注释;再次按下Ctrl+/(command+/),注释被取消….

  • matlab测试部分,验证、确认和测试 – MATLAB 和 Simulink 解决方案 – MATLAB & Simulink

    matlab测试部分,验证、确认和测试 – MATLAB 和 Simulink 解决方案 – MATLAB & Simulink请选择其一AlabamaAlaska美属萨摩亚APO/FPOAAAPO/FPOAEAPO/FPOAPArizonaArkansasCaliforniaCarolineIslandsColoradoConnecticutDelawareDistrictofColumbiaFlorida格鲁吉亚关岛HawaiiIdahoIllinoisIndianaIowaKansasKentuckyLo…

  • Java基础篇:四种代码块详解

    Java基础篇:四种代码块详解

发表回复

您的电子邮箱地址不会被公开。

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