Hessian 序列化、反序列化

news/2024/7/24 8:47:07 标签: java, 编程语言, jdk, android, .net

动手点关注干货不迷路 👆

背景

问题和思考:

  1. 序列化参数有枚举属性,序列化端增加一个枚举,能否正常反序列化?

  2. 序列化子类,它和父类有同名参数,反序列化时,同名参数能否能正常赋值?

  3. 序列化对象增加参数,反序列化类不增加参数,能否正常反序列化?

  4. 用于序列化传输的属性,用包装器比较好,还是基本类型比较好?

为什么要使用序列化和反序列化

  1. 程序在运行过程中,产生的数据,不能一直保存在内存中,需要暂时或永久存储到介质(如磁盘、数据库、文件)里进行保存,也可能通过网络发送给协作者。程序获取原数据,需要从介质,或网络传输获得。传输的过程中,只能使用二进制流进行传输。

  2. 简单的场景,基本类型数据传输。通过双方约定好参数类型,数据接收方按照既定规则对二进制流进行反序列化。

5728ae10eac022ba36bd6c40293445a7.png
  1. 复杂的场景,传输数据的参数类型可能包括:基本类型、包装器类型、自定义类、枚举、时间类型、字符串、容器等。很难简单通过约定来反序列化二进制流。需要一个通用的协议,共双方使用,进行序列化和反序列化。

三种序列化协议及对比

序列化协议特点
jdk
jdk 自带)
1. 序列化:除了 static、transient类型
2. 特点:强类型,安全性高,序列化结果携带类型信息
3. 反序列化:基于 Field 机制
4. 应用场景:深拷贝
fastjson
(第三方实现)
1. 可读性好,空间占用小
2. 特点:弱类型,序列化结果不携带类型信息,可读性强。有一些安全性问题
3. 反序列化:基于 Field 机制,兼容 Bean 机制
4. 应用场景:消息、透传对象
hessian
(第三方实现)
1. 序列化:除了 static、transient 类型
2. 特点:强类型,体积小,可跨语言,序列化结果携带类型信息
3. 反序列化:基于 Field 机制,兼容 Bean 机制
4. 应用场景:RPC

对比

Father father = new Father();
father.name = "厨师";
father.comment = "川菜馆";
father.simpleInt = 1;
father.boxInt = new Integer(10);
father.simpleDouble = 1;
father.boxDouble = new Double(10);
father.bigDecimal = new BigDecimal(11.5);

运行结果:

jdk序列化结果长度:626,耗时:55
jdk反序列化结果:Father{version=0, name='厨师', comment='川菜馆', boxInt=10, simpleInt=1, boxDouble=10.0, simpleDouble=1.0, bigDecimal=11.5}耗时:87

hessian序列化结果长度:182,耗时:56
hessian反序列化结果:Father{version=0, name='厨师', comment='川菜馆', boxInt=10, simpleInt=1, boxDouble=10.0, simpleDouble=1.0, bigDecimal=11.5}耗时:7

Fastjson序列化结果长度:119,耗时:225
Fastjson反序列化结果:Father{version=0, name='厨师', comment='川菜馆', boxInt=10, simpleInt=1, boxDouble=10.0, simpleDouble=1.0, bigDecimal=11.5}耗时:69

分析:

  • jdk 序列化耗时最短,但是序列化结果长度最长,是其它两种的 3 ~ 5 倍。

  • fastjson 序列化结果长度最短,但是耗时是其它两种的 4 倍左右。

  • hessian 序列化耗时与 jdk 差别不大,远小于 fastjson 序列化耗时。且与 jdk 相比,序列化结果占用空间非常有优势。另外,hessian 的反序列化速度最快,耗时是其它两种的 1/10。

  • 综上比较,hessian 在序列化和反序列化表现中,性能最优。

Hessian 序列化实战

实验准备

父类

public class Father implements Serializable {

    /**
     * 静态类型不会被序列化
     */
    private static final long serialVersionUID = 1L;

    /**
     * transient 不会被序列化
     */
    transient int version = 0;

    /**
     * 名称
     */
    public String name;

    /**
     * 备注
     */
    public String comment;

    /**
     * 包装器类型1
     */
    public Integer boxInt;

    /**
     * 基本类型1
     */
    public int simpleInt;

    /**
     * 包装器类型2
     */
    public Double boxDouble;

    /**
     * 基本类型2
     */
    public double simpleDouble;

    /**
     * BigDecimal
     */
    public BigDecimal bigDecimal;

    public Father() {
    }

    @Override
    public String toString() {
        return "Father{" +
                "version=" + version +
                ", name='" + name + '\'' +
                ", comment='" + comment + '\'' +
                ", boxInt=" + boxInt +
                ", simpleInt=" + simpleInt +
                ", boxDouble=" + boxDouble +
                ", simpleDouble=" + simpleDouble +
                ", bigDecimal=" + bigDecimal +
                '}';
    }
}

子类

public class Son extends Father {

    /**
     * 名称,与father同名属性
     */
    public String name;

    /**
     * 自定义类
     */
    public Attributes attributes;

    /**
     * 枚举
     */
    public Color color;

    public Son() {
    }

}

属性-自定义类

public class Attributes implements Serializable {

    private static final long serialVersionUID = 1L;

    public int value;

    public String msg;

    public Attributes() {
    }

    public Attributes(int value, String msg) {
        this.value = value;
        this.msg = msg;
    }

}

枚举

public enum Color {

    RED(1, "red"),
    YELLOW(2, "yellow")
    ;

    public int value;

    public String msg;

    Color() {
    }

    Color(int value, String msg) {
        this.value = value;
        this.msg = msg;
    }
}

使用到的对象及属性设置

Son son = new Son();
son.name = "厨师";    // 父子类同名字段,只给子类属性赋值
son.comment = "川菜馆";
son.simpleInt = 1;
son.boxInt = new Integer(10);
son.simpleDouble = 1;
son.boxDouble = new Double(10);
son.bigDecimal = new BigDecimal(11.5);
son.color = Color.RED;
son.attributes = new Attributes(11, "hello");

运行结果分析

使用 Hessian 序列化,结果写入文件,使用 vim 打开。使用 16 进制方式查看,查看命令:%!xxd

00000000: 4307 6474 6f2e 536f 6e9a 046e 616d 6504  C.dto.Son..name.
00000010: 6e61 6d65 0763 6f6d 6d65 6e74 0662 6f78  name.comment.box
00000020: 496e 7409 7369 6d70 6c65 496e 7409 626f  Int.simpleInt.bo
00000030: 7844 6f75 626c 650c 7369 6d70 6c65 446f  xDouble.simpleDo
00000040: 7562 6c65 0a61 7474 7269 6275 7465 7305  uble.attributes.
00000050: 636f 6c6f 720a 6269 6744 6563 696d 616c  color.bigDecimal
00000060: 6002 e58e a8e5 b888 4e03 e5b7 9de8 8f9c  `.......N.......
00000070: e9a6 869a 915d 0a5c 430e 6474 6f2e 4174  .....].\C.dto.At
00000080: 7472 6962 7574 6573 9205 7661 6c75 6503  tributes..value.
00000090: 6d73 6761 9b05 6865 6c6c 6f43 0964 746f  msga..helloC.dto
000000a0: 2e43 6f6c 6f72 9104 6e61 6d65 6203 5245  .Color..nameb.RE
000000b0: 4443 146a 6176 612e 6d61 7468 2e42 6967  DC.java.math.Big
000000c0: 4465 6369 6d61 6c91 0576 616c 7565 6304  Decimal..valuec.
000000d0: 3131 2e35 0a                             11.5.

对其中的十六进制数逐个分析,可以拆解为一下结构:参考 hessian 官方文档,链接:http://hessian.caucho.com/doc/hessian-serialization.html

序列化原理

0eb23d45fb9c847d9bad93bf6d9ecaf2.png

序列化规则:

  1. 被序列化的类必须实现了 Serializable 接口

6df3aa0f418f1a76eda1634f6b79d58a.png
  1. 静态属性和 transient 变量,不会被序列化。

deba092923ccfbb4a694da3e7fccc1f9.png
  1. 枚举类型在序列化后,存储的是枚举变量的名字

  2. 序列化结果的结构:类定义开始标识 C -> 类名长度+类名 -> 属性数量 -> (逐个)属性名长度+属性名 -> 开始实例化标识 -> (按照属性名顺序,逐个设置)属性值(发现某个属性是一个对象,循环这个过程)

6f9412add07aa335ee1560f3e89b8f67.png

反序列化

7bb1923c2f9489224840091738edb48d.png

通俗原理图:

fc59df4ed46caf7db07c05be9d1f764a.png db51e65bfaaf7cbac875cee592aa58ed.png

解释:这是前边的序列化文件,可以对着这个结构理解反序列化的过程。

87c71c4a775ed2eba0c94ce72bed61a1.png

解释:读取到“C”之后,它就知道接下来是一个类的定义,接着就开始读取类名,属性个数和每个属性的名称。并把这个类的定义缓存到一个_classDefs 的 list 里。

76f72ea1da43c8df1e1b0682646371ff.png

解释:通过读取序列化文件,获得类名后,会加载这个类,并生成这个类的反序列化器。这里会生成一个_fieldMap,key 为反序列化端这个类所有属性的名称,value 为属性对应的反序列化器。

356977d3416290f72b3da67fc7514c46.png

解释:读到 6 打头的 2 位十六进制数时,开始类的实例化和赋值。

遗留问题解答:

  • 增加枚举类型,反序列化不能正常读取。

ca3e7a44b0651c1d0c205f544c8d9703.png
    • 原因:枚举类型序列化结果中,枚举属性对应的值是枚举名。反序列化时,通过枚举类类名+枚举名反射生成枚举对象。枚举名找不到就会报错。

  • 反序列化为子类型,同名属性值无法正常赋值。

e5b5c0e5d511fd4a43b1d4ec163dbf67.png 30b399d8cbc0cc9f26045e1a4f31fc5a.png d70f1c6892a81054cacf03d60acab35a.png
  • 序列化对象增加参数,反序列化可以正常运行。

d3321fef094118e68bead78e8cd69904.png
    • 原因:反序列化时,是先通过类名加载同名类,并生成同名类的反序列化器,同名类每个属性对应的反序列化器存储在一个 map 中。在反序列化二进制文件时,通过读取到的属性名,到 map 中获取对应的反序列化器。若获取不到,默认是 NullFieldDeserializer.DESER。待到读值的时候,仅读值,不作 set 操作

  • 序列化和反序列化双方都使用对象类型时,更改属性类型,若序列化方不传输数据,序列化结果是‘N’,能正常反序列化。但是对于一方是基本类型,更改属性类型后,因为 hessian 对于基本类型使用不同范围的值域,所以无法正常序列化。


参考文档:

  • https://zhuanlan.zhihu.com/p/44787200

  • https://paper.seebug.org/1131/

  • hessian 官方文档:序列化规则http://hessian.caucho.com/doc/hessian-serialization.html#anchor10

  • ASCII 编码对照表http://ascii.911cha.com/


http://www.niftyadmin.cn/n/718532.html

相关文章

[转]web标准的几个误区

非常高兴地看到很多设计师开始关注和尝试使用web标准制作网页。但从网友们的问题和制作中发现几个问题,在这里特别提醒一下: 1.不是为了通过校验才标准化。web标准的本意是实现内容(结构)和表现分离,就是将样式剥离出来放在单独的css文件中。…

Mysql5.6优化方法

Mysql5.6优化方法 什么是优化? 合理安排资源、调整系统参数使MySQL运行更快、更节省资源。优化是多方面的,包括查询、更新、服务器等。原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。 数据库性能参数 使用S…

抖音 iOS 推荐 Feed 容器化总结

动手点关注干货不迷路 👆背景抖音 Feed 容器在推荐、关注、同城、朋友等多个场景中使用,每个场景都有自身的逻辑和业务,最终汇总在 FeedViewController 中,随着业务的迭代,代码越来越臃肿,面临如下的问题&a…

tomcat 优化操作

tomcat 优化操作 目的:通过优化tomcat提高网站的并发能力。 配置tomcat管理员账户 在conf/ tomcat-users.xml下添加用户&#xff1a; <role rolename"manager"/> <role rolename"manager-gui"/> <role rolename"admin"/> &l…

初学ASP.Net时在论坛收藏收集的一些资料备忘[转]

转自&#xff1a;http://dev.csdn.net/Develop/article/74/74156.shtm1. 打开新的窗口并传送参数&#xff1a; 传送参数&#xff1a;response.write("<script>window.open(*.aspx?id"this.DropDownList1.SelectIndex"&id1"...")</scrip…

Golang 中 map 探究

动手点关注 干货不迷路 &#x1f446;简介本文主要通过探究在golang 中map的数据结构及源码实现来学习和了解map的特性&#xff0c;共包含map的模型探究、存取、扩容等内容。欢迎大家共同讨论。Map 的底层内存模型在 golang 的源码中表示 map 的底层 struct 是 hmap&#xff0c…

智能插帧,打造丝滑视频体验

动手点关注 干货不迷路 &#x1f446;暑假期间小跳出去旅游&#xff0c;沿途用手机记录下了许多美丽景色。回家路上&#xff0c;小跳打开手机想用慢倍速去细细品味自己拍摄的视频&#xff0c;发现视频强烈的卡顿感让其“黯然失色”。失望的小跳想起前段时间在抖音上看到的剪映 …

五大手机增值业务短信发展空间最小

SMS、 WAP、 IVR、 CRBT、 JAVA等增值业务&#xff0c;被业界看作是我国手机增值服务领域的五大增值服务&#xff0c;日前&#xff0c;诺盛电信咨询对这几大电信增值业务的发展状况做出了分析&#xff0c;认为短信业务的发展空间最小。◆SMS&#xff1a;平稳增长 近两年我国对电…