枚举在hessian序列化和反序列化中的问题

Dubbo反序列化异常:ClassNotFound问题深度解析与解决方案 在分布式微服务架构中,远程过程调用(RPC)是实现服务间通信的核心技术,其底层依赖于序列化与反序列化机制将对象转换为二进制流进行网络传输。序列化协议(如Hessian2、Kryo)负责定义转换规则,其核心原理在于确保数据在跨进程、跨网络传输后的完整性与一致性。这一机制的技术价值在于解耦服务、提升系统扩展性,但同时也引入了接口契约管理的复杂性,尤其在服务多版本并行、灰度发布等应用场景下,极易因类定义不一致而引发兼容性问题。本文聚焦于Dubbo框架中典型的“Fail to decode request”异常,深 阅读详情

转自:http://yangbolin.cn/2016/05/22/enum-probolems-in-hessian/

一.概述

最近在拆分一个枚举类,但是该枚举类使用在一个RPC接口上,枚举类使用在RPC接口上,必然要考虑序列化和反序列化的问题,需要确保自己对枚举的拆分不会导致序列化和反序列化的问题。

原来的代码为

public enum xxxEnum {
  X("a11","a22"),
  Y("a111","a222"); 
  private String a1;
  private String a2;
  public String getA1(){ return a1;}
  public String getA2(){ return a2;}
 }

修改后的代码为

public enum xxxEnum {
  X("b11","b22"),
  Y("b111","b222"); 
  private String b1;
  private String b2;
  public String getB1(){ return b1;}
  public String getB2(){ return b2;}
 }

变更了两个成员变量的名字。这样修改反序列化会不会有问题?

二.问题分析

要想知道序列化和反序列化会不会有问题,得先看看序列化和反序列化的源码,关于hessian的序列化和反序列化有很内容,这里就拿枚举这一个点来分析。

先看看枚举序列化的代码

public class EnumSerializer extends AbstractSerializer {
    private Method _name;

    public EnumSerializer(Class cl) {
        // hessian/32b[12], hessian/3ab[23]
        if (!cl.isEnum() && cl.getSuperclass().isEnum())
            cl = cl.getSuperclass();

        try {
      // 通过反射来获取枚举类的name方法
            _name = cl.getMethod("name", new Class[0]);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void writeObject(Object obj, AbstractHessianOutput out) throws IOException {
        if (out.addRef(obj))
            return;

        Class cl = obj.getClass();

        if (!cl.isEnum() && cl.getSuperclass().isEnum())
            cl = cl.getSuperclass();

        String name = null;
        try {
      //调用枚举类的name方法来生成枚举序列化的值
            name = (String) _name.invoke(obj, (Object[]) null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        out.writeMapBegin(cl.getName());
        out.writeString("name");
        out.writeString(name);
        out.writeMapEnd();
    }
}

关于枚举的序列化可以总结为调用枚举类的name方法来生成序列化的字符串。


再看看枚举反序列化的代码

public class EnumDeserializer extends AbstractDeserializer {
  private Class _enumType;
  private Method _valueOf;
  
  public EnumDeserializer(Class cl)
  {
    // hessian/33b[34], hessian/3bb[78]
    if (cl.isEnum())
      _enumType = cl;
    else if (cl.getSuperclass().isEnum())
      _enumType = cl.getSuperclass();
    else
      throw new RuntimeException("Class " + cl.getName() + " is not an enum");

    try {
      // 反射获取枚举类的valueOf方法
      _valueOf = _enumType.getMethod("valueOf",
           new Class[] { Class.class, String.class });
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  
  public Class getType()
  {
    return _enumType;
  }
  
  public Object readMap(AbstractHessianInput in)
    throws IOException
  {
    String name = null;
    
    while (! in.isEnd()) {
      String key = in.readString();

      if (key.equals("name"))
        name = in.readString();
      else
  in.readObject();
    }

    in.readMapEnd();

    Object obj = create(name);
    
    in.addRef(obj);

    return obj;
  }
  
  @Override
  public Object readObject(AbstractHessianInput in, Object []fields)
    throws IOException
  {
    String []fieldNames = (String []) fields;
    String name = null;

    for (int i = 0; i < fieldNames.length; i++) {
      if ("name".equals(fieldNames[i]))
        name = in.readString();
      else
  in.readObject();
    }

    Object obj = create(name);

    in.addRef(obj);

    return obj;
  }

  private Object create(String name)
    throws IOException
  {
    if (name == null)
      throw new IOException(_enumType.getName() + " expects name.");

    try {
      //反射调用枚举类的valueOf方法
      return _valueOf.invoke(null, _enumType, name);
    } catch (Exception e) {
      throw new IOExceptionWrapper(e);
    }
  }
}

枚举的反序列化可以总结为反射调用枚举的valueOf方法来获取最终的的枚举值。

有了上面对枚举序列化反序列化源码的分析,现在我们看看相关的问题。(假定服务端做的序列化,客户端做的是反序列化,方便描述)

1.服务端枚举多了一个枚举值
假如服务端的枚举类为

public enum A {
  X,
  Y,
  Z;
}

客户端的枚举类为

public enum A {
  X,
  Y;
}

如果服务端返回一个A.Z给客户端,此时hessian反序列化调用枚举类的valueOf方法来获取反序列化,但是客户端的枚举类中没有Z,那么客户端反序列化直接跑异常。

2.服务端枚举ordinal值以及枚举类成员变量值和客户端不一致
假设服务端的枚举类为

public enum A {
  X("aaa"),
  Y("bbbb");//此时Y的ordinal为1,对应的value为bbb
  String value;
  A(String value) {this.value=value}
}

客户端的枚举类为

public enum A {
  X("aaa"),
  Z("ccc"),
  Y("ddd");// 此时Y的ordinal为2对应的value为ddd
  String value;
  A(String value) {this.value=value;}
}

假如入服务端传递给客户端的是A.Y,此时客户端拿到的A.Y对应的ordinal为2,对应的value为ddd。
上面这个点非常重要。

3.枚举是单例的

public enum TestEnum {
    
    XX("xx");
    
    TestEnum(String value) {
        this.value = value;
    }
    
    String value;

    
    public String getValue() {
        return value;
    }

    
    public void setValue(String value) {
        this.value = value;
    }
}
public class Test {
    
    public static void main(String[] rgs) {
        TestEnum testEnum1 = TestEnum.XX;
        TestEnum testEnum2 = TestEnum.XX;
        
        testEnum1.setValue("XX");
        testEnum2.setValue("YY");
        System.out.println(testEnum1.value); // 输出 YY
        System.out.println(testEnum2.value); // 输出 YY
    }
}

testEnum1和testEnum2其实指向了同一个枚举引用。每次修改的都是同一个对象,所以前一个set的值被后面的set给覆盖了。

三.总结

  • 还是不要在RPC的接口中直接使用枚举类了,直接使用String就行
  • 在枚举类中使用字符串时直接使用name()就行,不要再做过度封装,尽量保持枚举类的简洁
  • 枚举类使用在RPC接口上的时候就一定要小心,重构的时候要注意保持ordinal
  • 枚举在序列化和反序列化的时候,除了name值,其他啥都不带的
  • 禁止给枚举提供set方法,没用的

 

Ubuntu终端AI编程助手配置:Claude Code接入DeepSeek模型全攻略 在软件开发领域,AI编程助手正成为提升开发效率的重要工具。其核心原理是通过大语言模型理解自然语言指令,生成、解释或调试代码。这类工具的技术价值在于将AI能力无缝集成到开发工作流中,减少上下文切换,实现更流畅的人机协作。常见的应用场景包括代码生成、错误调试、代码重构和技术问答等。本文聚焦于在Ubuntu系统中,将流行的终端AI助手Claude Code的后端模型从原版Claude替换为DeepSeek。通过配置环境变量实现API端点和模型的映射,这一过程涉及Node.js环境管理、npm包安装和关键环境变量设 阅读详情

相关推荐

现控报告-- 分析倒立摆系统稳定性、能控性及能观性分析,设计PID控制方案(附matlab)

建立直线一级倒立摆系统的动力学模型,推导该系统的运动方程,求出直线一级倒立摆系统传递函数模型及空间状态方程模型。接着描述了PID控制系统设计原理及PID控制器各校正环节的作用,简要叙述了参数的整定方法。设计了直线一级倒立摆摆杆摆角及小车位置常规单回路控制器,通过Simulink仿真知道常规单回路控制器能控制摆杆摆角,不能控制小车位置。于是设计出了直线一级倒立摆双闭环控制器及基于倒立摆系统的状态空间方程控制器,利用软件进行仿真,仿真结果表明,常规双闭环控制方案和基于状态空间方程加控制方案均能对摆杆摆角及小车位

mengshenglo的博客 4万+

序列化枚举类型时出现枚举值无效,无法(反)序列化

枚举类型和int类型一样,当创建对象时默认为 0, 若要将枚举值给定一个int值,那么0就必须有。即使用不上它,也要保留它 public enum RunState { /// <summary> /// 初始 /// </summary> Init = 0, /// <summary> /// 未调用 /// </summary>

qq_40197926的博客 2159

从412到200:一步步拆解瑞数6代jsvmp的cookie生成与验证流程

本文深入解析瑞数6代JSVMP的动态安全防护机制,详细拆解从412到200状态的cookie生成与验证流程。通过剖析JSVMP虚拟机核心机制、动态元数据解析及环境检测绕过策略,为逆向工程师提供实用的技术解决方案,帮助突破瑞数6代的高级防御体系。

weixin_30342209的博客 508

枚举深入,包括序列化安全/反射安全

开始前关于枚举的一些问题 1.枚举允许继承类吗? 2.枚举允许实现接口吗? 3.枚举可以用等号比较吗? 4.可以继承枚举吗? 5.枚举可以实现单例模式吗? 6.当使用compareTo()比较枚举时,比较的是什么? 7.当使用equals()比较枚举的时候,比较的是什么? 枚举是什么? 枚举是JDK1.5新增的一种数据类型 枚举类是一种特殊的类,它和普通的类一样,有自己的成员变量、成员方法、构...

qq_36879870的博客 365

深入解析Xilinx IDELAYE2在FPGA时序优化中的关键作用与实战配置

本文深入解析了Xilinx FPGA中IDELAYE2模块在时序优化中的关键作用。作为精密的信号延时微调器,IDELAYE2通过可编程的抽头延迟,有效解决高速接口中数据与时钟的相位偏差问题。文章详细阐述了其必须与IDELAYCTRL配对使用的核心机制,并对比分析了FIXED、VARIABLE和VAR_LOAD三种工作模式的实战配置方法与代码示例,为FPGA工程师提供了解决时序收敛难题的关键硬件配置指南。

pear55的博客 1184

VSCode 远程开发:实现服务器图形化界面(GUI)的高效操作指南

本文详细介绍了如何通过VSCode远程开发实现服务器图形化界面(GUI)操作,包括X11转发原理、完整配置指南和实战测试。通过安装Xming、配置SSH连接和服务器端设置,开发者可以高效地在本地显示远程GUI应用,提升开发效率。

weixin_29245767的博客 294

hessian序列化源码分析

背景     最近在处理一个hessian的反序列化问题时,因为服务端使用了pojo bean中多了一个enum属性,导致客户端在反序列化时疯狂的在打印日志。警告说找不到对应的enum class,因为项目中本身是设置了log4j的根输出为一个文件。      比较奇怪的是,hessian对应的日志输出全都打印到了控制台(虽然我们对console进行了重定向输出),导致对应的文件达到几百...

agapple 563

Tribon Vitesse开发教程(一)

Tribon Vitesse简介 Tribon Vitesse是Tribon提供给Python编程语言的编程接口,它是一系列的API。使用Python的语法调用这些API,结合Python语言自身的语法生成的程序叫做Vitesse程序。Vitesse是法语“速度”的意思,读作“为戴斯”。使用Tribon Vitesse可以直接访问Tribon产品信息模型和使用Tribon应用程序的功能,以控制设计

forevermemory的专栏 2593

java 枚举 反序列化,Enum反序列化问题

1.Enum原理定义一个Enum,通过编译之后的字节码,我们可以发现其实现原理:public enum FruitEnum {APPLE, ORAGE}编译器是在为我们创建一个类,这个类继承自 java.lang.Enum,有两个公共的、静态的、被声明成final的属性,它们的类型就是我们定义的FruitEnum。编译器还生成了一个静态初始话器,就是字节码中static{};这一行下面的代码,其中...

weixin_33517544的博客 3651

Parasoft C++Test软件单元测试_操作指南

本文主要为使用C++TEST进行单元测试的流程性操作讲解,后续文章将以专题形式进行详细讲解,以及用一些被测函数作为示例进行实例讲解。

文章主要涉及嵌入式软件开发、测试、项目管理等,欢迎交流,共同进步。 1577

中台战略-第五章、中台建设方法论

文章目录第五章、中台建设方法论5.1 中台架构整体策略5.2 业务顶层设计5.3 业务中台设计方法论5.3.1 能力支撑是基础5.3.2 中心自治是承载形式5.3.3 3层模型是骨架5.3.4 5步法是指导思想1. 业务抽象2. 高阶设计3. 组件建模4. 开发交付5. 持续运营5.4 数据中台设计方法论5.4.1 总体规划5.4.2 数据集成5.4.3 模型建设5.4.4 通用研发5.4.5 资产管理5.4.6 数据服务5.5 应用向中台迁移的3中途径和方法5.5.1 方法1:新应用替换旧应用5.5.2

沧海之巅的专栏 1461

hession 源码解析 自定义序列化器

背景 最近在处理一个hessian的反序列化问题时,因为服务端使用了pojo bean中多了一个enum属性,导致客户端在反序列化时疯狂的在打印日志。警告说找不到对应的enum class,因为项目中本身是设置了log4j的根输出为一个文件。 比较奇怪的是,hessian对应的日志输出全都打印到了控制台(虽然我们对console进行了重定向输出),导致对应的文件达到几百MB。无...

xiaoliuliu2050的专栏 1021

Keil uVision5 STM32 startup_stm32f10x_md_vl.s error A3906U Malformed via file target not created..

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 2780

Hessian 序列化、反序列化

动手点关注干货不迷路????背景问题和思考:序列化参数有枚举属性,序列化端增加一个枚举,能否正常反序列化?序列化子类,它和父类有同名参数,反序列化时,同名参数能否能正常赋值?序列化对象增加参数,反序列化类不增加参数,能否正常反序列化?用于序列化传输的属性,用包装器比较好,还是基本类型比较好?为什么要使用序列化和反序列化程序在运行过程中,产生的数据,不能一直保存在内存中,需要暂...

字节跳动技术团队官方博客 5765

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Hessian和Java反序列化问题小结

转载地址:http://hittyt.iteye.com/blog/1691772#bc2368273   Hessian反序列化问题 众所周知,Hessian框架提供的序列化方式,在性能上要优于Java自己的序列化方式。他将对象序列化,生成的字节数组的数量要相对于Java自带的序列化方式要更简洁。 目前公司的一个项目中,有RPC调用的需要,这里我们使用了公司自己的开源

xubo_zhang的专栏 1万+

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Dubbo通过Hessian反序列化对象失败解决

大伙使用过dubbo的同学都知道,dubbo远程调用服务原理是通过序列化、反序列化实现的,这里不多说了问题描述:有一次写dubbo接口,需要抛出自定义异常,该异常只有一个构造方法(只有一个参数,为自定义枚举类型),下面是构造方法详情 public RefundOrderException(RefundOrderExceptionCode code) { super(code....

点滴 1万+

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Hessian2 序列化, 与 Protobuf 相比如何?

Protobuf 和 Hessian2 都是高性能的二进制序列化协议,各自具有一定的优缺点。Hessian2 相较于原始 Hessian 协议,引入了一些改进和优化,使得序列化性能更高、协议更紧凑。由于上述优点,Hessian2 在分布式系统和微服务架构中常被用作高效的序列化协议。在 Dubbo 中,Hessian2 是默认的序列化协议之一,可以通过配置启用或更换为其他序列化协议。要在 Java 中使用 Hessian2 进行序列化和反序列化,首先需要引入相关依赖。类进行序列化和反序列化操作。

洪晓鸿 2575

Hessian2序列化支持这个feature,让我们重构Dubbo接口更加容易

我们知道,dubbo RPC默认序列化方式是Hessian2。 ????先看如下Hessian2序列化的测试代码。 // ------- MyDto ------- import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; @Data @Accessors(chain = true)...

gzyes 353

Java序列化和Hessian序列化问题

Java序列化:   Java序列化会把要序列化的对象类的元数据和业务数据全部序列化为字节流,而且是把整个继承关系上的东西全部序列化了。它序列化出来的字节流是对那个对象结构到内容的完全描述,包含所有的信息,因此效率较低而且字节流比较大。但是由于确实是序列化了所有内容,所以可以说什么都可以传输,因此也更可用和可靠。 hession序列化:   它的实现机制是着重于数据,附带简单的类型信息的方...

luzhensmart的专栏 1365

dubbo序列化源码分析2-深入JDK,hessian序列化

JDK序列化 实现Serializable接口 被序列化的Object中的 Object属性必须实现 Serializable接口,否则报异常, 基本类型默认可以序列化。static 和transient关键字修饰的变量不会被实例化 父类A没有实现Serializable 接口,子类B实现了Serializable 接口, A a=new B() 此时a可以序列化 A a=new A() 此时 a不可以序列化,也就是 被序列化的是指向的是实现了 Serializable 接口的实例才可以。 .

chenchangqun11的专栏 621

Dubbo反序列化异常排查:从原理到实战解决Fail to decode request

在分布式微服务架构中,远程过程调用(RPC)是实现服务间通信的核心技术,其性能与稳定性直接关系到整个系统的可用性。RPC框架通过序列化机制将对象转换为字节流进行网络传输,接收方再反序列化还原,这一过程的可靠性是保障服务调用的基础。序列化协议的选择与一致性、参数类型的兼容性以及网络传输的完整性,共同构成了RPC调用的技术价值,决定了服务在高并发、多版本并行等复杂场景下的表现。当序列化或反序列化环节出现异常,例如遇到参数类型不匹配、数据损坏或协议不一致等问题时,就会引发服务调用失败,直接影响业务连续性。本文聚焦

weixin_34067102的博客 559
上一篇: int转byte与补码的关系
下一篇: Git全局递归忽略.DS_Store(解决Mac下统一递归的忽略.DS_Store的方法)[亲测有效]
longhee
博客等级 码龄14年 2粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值