了解 dubbo 序列化,从 bug 开始

首先交代背景,前几天遇到一个小bug,由于其他系统的一个DTO子类和父类有一个字段名重复了,所以导致我set的子类字段那边拿不到值。改起来很简单嘛,让对面把子类的字段删掉就好,但是拿不到值的原因让我想了很久,很明显是序列化和反序列化的过程中这个字段的值丢失了,但是到底是在哪一步呢?还是决定看看源码给自己一个答案。

dubbo 版本:2.6.7。

首先先确定协议和序列化的方式:

<dubbo:protocol name="dubbo" port="${dubbo.port}" host="${dubbo.host}"/>

ok,dubbo协议,没有配序列化方式,那再来找一下dubbo默认的序列化方式。

首先找到了这个包:

然后在Serialization接口(实现一些自定义序列化扩展用的)中发现了默认序列化的方式是hessian2

然后通过Hessian2Serializationserialize方法和deserialize方法找到了Hessian2ObjectOutputHessian2ObjectInput这个两个类,之后通过里面的writeObject方法和readObject方法,找到了这两个类:JavaSerializerJavaDeserializer。就是默认的序列化器和反序列化器。

首先我们来看序列化。构造方法:

我们看看他干了个什么事:

  1. 首先检查了有没有writeReplace方法
  2. 然后把所有声明的字段放到数组中遍历,然后把除了transient和static修饰的字段放到ArrayList中去
  3. 然后往上找父类执行相同操作
  4. 然后把所有符合条件的字段放到一个Field数组里面,先放基本数据类型,再放引用数据类型
  5. 最后把字段放入对应类型的序列化器中去,得到一个FieldSerializer序列化器数组

我们还是继续看writeObject方法:

主要是第二张图的3个方法,做的事情大概就是循环序列化field的名和值。

下面再来看看反序列化。构造方法:

这里大概做了3个事:

1、获取fieldMap,方法如下:

2、获取readResolve方法

3、获取所有构造器

遍历构造器数组找到cost最小的最佳构造器,然后用最佳构造器进行构造,方法如下:

先看构造器方法,基本数据类型的话返回包装类型,引用数据类型返回null。

主要我们来看获取fieldMap的时候。和序列化时相同,先取子类再取父类,本来以为到这就结束了,可是看到了一行校验:

fieldMap.get(field.getName()) == null

如果父类的字段名和子类字段名相同,会跳过该循环,也就是说只会有子类的值,那为什么还会覆盖?

看最后一个 readMap方法:

debug发现值被覆盖就是在这里操作的:

deser.deserialize(in, obj);

子类set过值以后,父类过来也会拿到子类的反序列化器,把子类的值覆盖掉。

最后,那为什么会到readMap这一步呢?

debug发现SerializerFactory拿到反序列化器后会执行一个readMap操作:

基本上就到这里了,最主要的原因还是readMap方法,并不是很多博文说的fieldMap。如果哪里有不对的地方欢迎指出、讨论以便及时修改。

参考文档:http://dubbo.apache.org/#!/docs/dev/impls/serialize.md?lang=zh-cn

原文发布于微信公众号 - 程序员宝库(chengxuyuanbaoku)

原文发表时间:2018-08-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发与安全

算法:静态查找表(Static Search Table)(顺序查找、二分查找、插值查找、斐波纳契查找)

查找表(Search table)是由同一类型的数据元素(或记录)构成的集合。关键字(key)是数据元素中某个数据项的值,又称为键值,用它可以表示一个数据元素,...

2365
来自专栏Python爬虫与算法进阶

学点算法之栈的学习与应用

在学习栈前,脑海中对这个词只有一个印象:客栈 栈是什么 栈(有时称为“后进先出栈”)是一个项的有序集合,其中添加移除新项总发生在同一端。 这段话初学者是懵逼的...

3093
来自专栏恰同学骚年

.NET基础拾遗(1)类型语法基础和内存管理基础

在.NET中所有的内建类型都继承自System.Object类型。在C#中,不需要显示地定义类型继承自System.Object,编译器将自动地自动地为类型添...

1122
来自专栏大闲人柴毛毛

Java8新特性——StreamAPI(一)

1. 流的基本概念 1.1 什么是流? 流是Java8引入的全新概念,它用来处理集合中的数据,暂且可以把它理解为一种高级集合。 众所周知,集合操作非常麻烦,若...

3599
来自专栏我是攻城师

使用JAVA反射的利与弊

3894
来自专栏LhWorld哥陪你聊算法

【Scala篇】--Scala中Trait、模式匹配、样例类、Actor模型

Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。

902
来自专栏青青天空树

struts返回json数据

  实际上就是在struts中获取response对象的输出流。然后写入你要返回的json数据,本质和用servlet返回json数据是一样的,需要自己导入js...

1386
来自专栏博客园

Core官方DI解析(4)--CallSiteRuntimeResolver

这两个类都在其CallSiteVisitor<TArgument, TResult>基类中

843
来自专栏软件开发 -- 分享 互助 成长

为什么无返回值的链表的插入操作头结点一定要用指向指针的指针

前言: 为什么链表的插入操作头结点一定要用指向指针的指针?之前自己对这个问题总是一知半解,今天终于花了点时间彻底搞懂了。 总的来说这样做的目的是为了应对“空链表...

2187
来自专栏GreenLeaves

C# Encoding

之前做公司项目的时候,对于C#编码这块总是一知半解,所以打算通过这篇笔记对C#编码(Encoding)进行彻底的扫盲,关于编码和字符集的基础知识,请参考字符集和...

2647

扫码关注云+社区

领取腾讯云代金券