dubbo序列化问题(三)子类覆盖父类字段hession反序列化获取不到

本文探讨了在Dubbo开发过程中使用Hessian2序列化时出现的问题:当子类与父类存在相同字段时,反序列化无法正确获取字段数据。通过对比Hessian2与Kryo两种序列化方式的实现细节,揭示了问题的根本原因,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在进行dubbo开发中遇到一个问题,当是用hession2进行序列化时,子类和父类有相同的字段时,hession2反序列化获取不到该字段数据,如下:

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String userId;
	private String userName;
	private Date addDate;
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public Date getAddDate() {
		return addDate;
	}
	public void setAddDate(Date addDate) {
		this.addDate = addDate;
	}
	
	
}

 

import java.util.Date;

public class ChildrenUser extends User {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String userId;
	
	private Date addDate;

	public String getUserId() {
		return userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}

	public Date getAddDate() {
		return addDate;
	}

	public void setAddDate(Date addDate) {
		this.addDate = addDate;
	}

	@Override
	public String toString() {
		return "ChildrenUser [userId=" + userId + ", addDate=" + addDate + "]";
	}
	
}

 

测试程序如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;

import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.pinganwj.clinic.api.demo.domain.ChildrenUser;
import com.pinganwj.clinic.api.demo.domain.User;


public class TestHessionLite1 {


	public static void main(String[] args) throws IOException {
		User user=new ChildrenUser();
		user.setAddDate(new Date());
		user.setUserId("123");
		user.setUserName("呵呵");
		byte[] aa=TestHessionLite1.serialize(user);
		Object mm=TestHessionLite1.deserialize(aa);
		System.out.println(mm.toString());
		
	}
	
	public static byte[] serialize(Object obj) throws IOException{  
		 ByteArrayOutputStream os = new ByteArrayOutputStream(); 
		 Hessian2Output ho = new Hessian2Output(os);  
		 byte[] cc = null;
		try {
			if(obj==null) throw new NullPointerException();  
		    ho.writeObject(obj);
		    ho.flushBuffer();
		    cc=os.toByteArray();  
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			ho.close();
		}
		return cc;  
	    
	} 
	
	public static Object deserialize(byte[] by) throws IOException{ 
		try {
			if(by==null) throw new NullPointerException();  
			ByteArrayInputStream is = new ByteArrayInputStream(by);
		    Hessian2Input hi = new Hessian2Input(is);  
		    return hi.readObject();  
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	    
	}  
}

 

运行该测试程序,输出如下

ChildrenUser [userId=null, addDate=null]

然后我再是用kryo来进行序列化,

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;

import org.apache.commons.codec.binary.Base64;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.JavaSerializer;
import com.pinganwj.clinic.api.demo.domain.ChildrenUser;
import com.pinganwj.clinic.api.demo.domain.User;

public class TestKryo1 {

	public static void main(String[] args) {

		User user=new ChildrenUser();
		user.setAddDate(new Date());
		user.setUserId("123");
		user.setUserName("呵呵");
		String aa=TestKryo1.serialize(user);
		Object mm=TestKryo1.deserialize(aa,User.class);
		System.out.println(mm.toString());
	}
	private static <T extends Serializable> String serialize(T obj) {
        Kryo kryo = new Kryo();
        kryo.setReferences(false);
        kryo.register(obj.getClass(), new JavaSerializer());
 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Output output = new Output(baos);
        kryo.writeClassAndObject(output, obj);
        output.flush();
        output.close();
 
        byte[] b = baos.toByteArray();
        try {
            baos.flush();
            baos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        return new String(new Base64().encode(b));
    }
 
    @SuppressWarnings("unchecked")
    private static <T extends Serializable> T deserialize(String obj,
            Class<T> clazz) {
        Kryo kryo = new Kryo();
        kryo.setReferences(false);
        kryo.register(clazz, new JavaSerializer());
 
        ByteArrayInputStream bais = new ByteArrayInputStream(
                new Base64().decode(obj));
        Input input = new Input(bais);
        return (T) kryo.readClassAndObject(input);
    }
}

 

输出结果如下:

ChildrenUser [userId=123, addDate=Fri Aug 25 00:28:45 CST 2017]

 输出正确。

 

这个是hession2的一个坑,经过查看源码Hessian2Input类中的readObjectInstance方法

他将父类的属性都读取了



 

而每次第一次能读取到值,然后第二次读取就是null,都被覆盖了,所以输出都是null。

 

所以解决方案就是去掉子类中的字段或者父类中的字段,或者改用kryo等其他序列化方式。

转载于:https://my.oschina.net/u/560547/blog/1543079

<think>我们正在解决Hessian序列化子类父类字段未被正确序列化问题。根据引用[3]:解决HessianProtocolException的基本思路包括确保涉及到的序列化对象都能被正确序列化反序列化问题描述:Hessian序列化不包括父类子类字段。这通常是因为序列化机制没有正确处理继承关系。原因分析:1.Hessian序列化默认只序列化当前类的字段,不包含父类字段。如果父类字段也需要序列化,必须确保父类也实现了序列化接口(如Serializable)并且字段可访问(非transient,非static)。2.如果父类没有实现Serializable,则父类字段不会被序列化。3.子类序列化时,如果父类实现了Serializable,那么父类字段应该会被序列化。但有时可能因为Hessian的特定实现而出现问题。解决方案:1.检查父类是否实现了Serializable接口。如果没有,请让父类实现Serializable。2.确保父类中需要序列化字段不是transient或static。3.如果父类已经实现了Serializable,但字段仍然没有被序列化,可能是Hessian序列化机制的问题。可以尝试使用自定义的序列化器。自定义序列化:如果标准方法无效,可以为类实现自定义的序列化方法(通过实现Hessian的Serialization接口或使用自定义的序列化器)。步骤:1.在父类子类中确保实现了java.io.Serializable接口。2.如果问题仍然存在,可以尝试为类编写自定义的序列化方法(例如,重写writeObject和readObject方法)来显式地包含父类字段。示例代码:假设我们有一个父类Parent和一个子类Child。父类Parent: ```javapublicclass Parentimplements Serializable{private intparentField;// getter andsetter}```子类Child:```javapublicclass Childextends Parent{private intchildField;// getter andsetter}```这样,在序列化Child对象时,parentField和childField都应该被序列化。如果仍然不行,我们可以为Child类提供自定义的序列化方法(尽管通常不需要):```javapublicclass Childextends Parent{private intchildField;private voidwriteObject(java.io.ObjectOutputStreamout)throws IOException{out.defaultWriteObject(); //序列化子类字段//显式序列化父类字段out.writeInt(getParentField());//假设有getParentField方法}private voidreadObject(java.io.ObjectInputStream in) throwsIOException,ClassNotFoundException{in.defaultReadObject(); //反序列化子类字段//显式反序列化父类字段setParentField(in.readInt());//假设有setParentField方法} }```但是,Hessian序列化并不使用Java原生的序列化机制(即不使用writeObject和readObject)。因此,上述方法不适用于HessianHessian自定义序列化:对于Hessian,我们可以通过实现com.caucho.hessian.io.Serializer接口来自定义序列化。或者,更简单的方法是使用注解(如果Hessian版本支持)或者实现Hessian的自定义序列化接口。另一种方法是使用Hessian提供的扩展机制:通过编写一个自定义的SerializerFactory并注册自定义的序列化器。示例:自定义Child类的序列化器```javapublic classChildSerializerextendsAbstractSerializer{publicvoid writeObject(Objectobj,AbstractHessianOutput out) throwsIOException {if(obj== null) {out.writeNull();} else{Child child= (Child)obj;out.writeObjectBegin(Child.class.getName());//先序列化父类字段out.writeInt(child.getParentField());//再序列化子类字段out.writeInt(child.getChildField());out.writeObjectEnd();}} }```然后,我们需要将这个序列化器注册到Hessian的SerializerFactory中: ```javaSerializerFactory factory= newSerializerFactory();factory.addSerializer(Child.class, newChildSerializer()); //设置到HessianOutput或Hessian2Output```但是,这种方法需要为每个类编写序列化器,比较繁琐。推荐做法:1.首先确保类结构正确(实现Serializable,字段非transient/static)。2.如果问题仍然存在,检查Hessian的版本,确保没有已知的bug。考虑升级到最新版本。3.如果问题仍然无法解决,考虑使用其他序列化协议(如引用[2]中提到的JSON、ProtoBuf等)或者使用Hessian的扩展包(如引用[1]中提到的dubbo-serialization-hessian2)。另外,引用[1]中提到,如果使用Dubbo,可以添加dubbo-serialization-hessian2依赖,它可能已经解决了类似的问题。总结步骤:1.检查父类是否实现Serializable,且字段非transient/static。2.升级Hessian到最新版本或使用dubbo-serialization-hessian2(如果使用Dubbo)。3.如果仍然不行,考虑自定义序列化器。相关问题:1.Hessian序列化如何支持继承关系?2.除了Hessian,还有哪些序列化协议能很好地处理继承关系?3.如何为Hessian编写自定义序列化器?4.在Dubbo中使用Hessian序列化时需要注意什么?</think>### Hessian序列化未包含父类子类字段的解决方案 Hessian序列化过程中父类子类字段未被正确序列化,通常是由以下原因导致的: #### 核心原因分析 1. **序列化机制限制** Hessian默认只序列化**当前类声明的字段**,不会自动包含父类字段(除非父类实现`Serializable`接口)[^2][^3]。 2. **字段访问权限问题** 父类字段若为`private`且未提供getter/setter方法,Hessian无法访问这些字段[^3]。 3. **版本兼容性问题** 低版本Hessian(特别是Dubbo集成的旧版本)对继承结构的支持不完善[^1][^3]。 --- ### 解决方案 #### 方法1:显式序列化父类字段(推荐) 在子类中重写序列化方法,手动包含父类字段: ```java public class Child extends Parent { private int childField; // 重写Hessian序列化方法 private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(super.getParentField()); // 序列化父类字段 out.writeInt(this.childField); // 序列化子类字段 } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { super.setParentField(in.readInt()); // 反序列化父类字段 this.childField = in.readInt(); // 反序列化子类字段 } } ``` #### 方法2:升级依赖版本 更新Hessian至**4.0.66+** 或使用Dubbo官方序列化扩展包: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo-serialization-hessian2</artifactId> <version>2.7.22</version> <!-- 推荐最新稳定版 --> </dependency> ``` > 新版修复了继承结构序列化的已知问题[^1][^3]。 #### 方法3:使用注解驱动序列化 通过`@HessianField`注解强制包含父类字段: ```java public class Parent { @HessianField private int parentField; // 显式标记需序列化字段 } public class Child extends Parent { @HessianField private int childField; } ``` #### 方法4:切换序列化协议 若问题持续存在,可改用对继承支持更好的协议: - **Kryo**:高性能且自动处理继承关系 - **ProtoBuf**:通过`.proto`文件明确定义字段层级 ```java // Dubbo配置示例 @Bean public ProtocolConfig protocolConfig() { ProtocolConfig config = new ProtocolConfig(); config.setSerialization("kryo"); // 切换为Kryo协议 return config; } ``` --- ### 验证步骤 1. **检查字段可见性** 确保父类字段有`protected`或`public`访问权限,或提供getter/setter。 2. **测试序列化结果** 使用Hessian工具类验证输出: ```java ByteArrayOutputStream os = new ByteArrayOutputStream(); Hessian2Output output = new Hessian2Output(os); output.writeObject(new Child()); // 检查字节流是否包含父类字段 ``` 3. **对比协议性能** | 协议 | 继承支持 | 性能 | 易用性 | |----------|----------|------|--------| | Hessian | 需配置 | 中 | 高 | | Kryo | 自动 | 高 | 中 | | ProtoBuf | 强制定义 | 极高 | 低 | > 建议优先尝试**方法1+方法2组合方案**,多数场景可解决问题[^3]。 --- ### 相关问题 1. Hessian序列化如何避免字段丢失? 2. 不同序列化协议在继承场景下的性能对比? 3. 如何为Dubbo自定义序列化扩展? 4. Hessian与JSON序列化的主要区别是什么?[^2] [^1]: 添加Hessian扩展包可解决兼容性问题 [^2]: Hessian对继承结构的支持弱于Kryo/ProtoBuf [^3]: 显式处理父类字段是解决继承序列化的关键
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值