`
DiaoCow
  • 浏览: 244387 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Hessian源码学习(五)JavaSerializer

阅读更多
紧接着上一篇,我们来看看另一个序列化类JavaSerializer(参考源码版本3.0.13)。
这个类一般是用来序列化我们自定义类的(DefaultSerializer),所以更多的时候我们用到的是它,今天打算分析两个方面:
1.JavaSerializer本身实现分析;
2.分析下反序列化的时候为什么会存在父类同名field覆盖子类field值问题;

JavaSerializer实现
// 首先看一下JavaSerializer的构造方法
public JavaSerializer(Class cl, ClassLoader loader)
{
	...
	// 存放简单类型字段 
    ArrayList primitiveFields = new ArrayList();
	// 存放复杂类型字段
    ArrayList compoundFields = new ArrayList();
    
	// 获取子类到父类所有field字段(这里你可能就会想,万一子类和父类存在同名filed怎么办?)
    for (; cl != null; cl = cl.getSuperclass()) {
      Field []fields = cl.getDeclaredFields();
      for (int i = 0; i < fields.length; i++) {
		Field field = fields[i];
		
		// 过滤掉这两种类型的field(可以思考下为什么要过滤掉这两种类型)
		if (Modifier.isTransient(field.getModifiers())
			|| Modifier.isStatic(field.getModifiers()))
		  continue;

		field.setAccessible(true);
	
		// 按field类型存入arrayList(若是基本类型或者是java.lang包下的类则当做是primitiveField,否则当做compoundField)
		if (field.getType().isPrimitive()
			|| (field.getType().getName().startsWith("java.lang.")
			&& ! field.getType().equals(Object.class)))
		  primitiveFields.add(field);
		else
		  compoundFields.add(field);
		  }
		}
	
	// 合并成一个list并转换成数组 (ps: 这样看来之前用两个list来存放似乎没有什么意义?!)
    ArrayList fields = new ArrayList();
    fields.addAll(primitiveFields);
    fields.addAll(compoundFields);

    _fields = new Field[fields.size()];
    fields.toArray(_fields);
}

接着我们看下JavaSerializer的writeObject方法:
public void writeObject(Object obj, AbstractHessianOutput out)
    throws IOException
{
	// 省略部分代码...
	
	Class cl = obj.getClass();
	
	try {

		// 写类头,输出格式: 'Mt' [类名长度] [类名]  譬如HelloWorld.java输出的就是:Mt 数值10 字符HelloWorld
		out.writeMapBegin(cl.getName());

		// 循环序列化每一个field
        for (int i = 0; i < _fields.length; i++) {
			Field field = _fields[i];
      
			// 序列化field名,格式:'S' [字段名长度] [字段名]
			out.writeString(field.getName());
			// 序列化filed值(内部获取该field对应的序列化类,然后执行序列化(其实又回到了Hessian源码学习(三)中所说的内容) 
			out.writeObject(field.get(obj));
		}
		// 输出结束标记:'z'
		out.writeMapEnd();
		
    } catch (IllegalAccessException e) {
		throw new IOException(String.valueOf(e));
    }
}

说了这么多,还是看一个例子比较直观:
序列化测试类(HelloWorld):
public class HelloWorld {

    private int       field1;

    public String     field2;

    protected boolean field3;

    short[]           field4;

序列化代码:
 
HelloWorld obj = new HelloWorld();

obj.setField1(1234);
obj.setField2("diaocow");
obj.setField3(false);
obj.setField4(new short[] { (short) 125, (short) 28 });

OutputStream os = new FileOutputStream("hessianOutput");
AbstractHessianOutput out = new HessianOutput(os);
out.setSerializerFactory(new SerializerFactory());
out.writeObject(obj);

序列化结果:(图画的不好,有空重新画一幅)



JavaSerializer存在的问题
之前在文章开始,我提到在序列化的时候存在父类覆盖子类同名field的情况,现在我们就来看下源码,
分析下为什么会出现这样的情况?(只是谈谈自己看法,若是有不对的地方,还希望各位能够提点一下,^_^)

JavaSerializer的实现我们前面已经分析,现在我们分析下JavaDeSerializer的实现(关键就在于序列化和反序列化的不一致引起的)
public JavaDeserializer(Class cl)
{
    ...
	
	// 最关键的一段代码
    _fieldMap = getFieldMap(cl);
	
	...
}

我们看下getFieldMap(cl)的实现:

protected HashMap getFieldMap(Class cl)
  {
	// 创建一个hashmap存放field (有没有觉得和JavaSerializer不一致,JavaSerializer是把field存放到一个list中去)
    HashMap fieldMap = new HashMap();
    
	// 循环读取从子类到父类的field
    for (; cl != null; cl = cl.getSuperclass()) {
      Field []fields = cl.getDeclaredFields();
      for (int i = 0; i < fields.length; i++) {
        Field field = fields[i];
		
        if (Modifier.isTransient(field.getModifiers()) ||
            Modifier.isStatic(field.getModifiers()))
          continue;
		// 重点! 由于是从子类循环到父类,所以若是存在同名字段,fieldMap中放的都是子类字段!
        else if (fieldMap.get(field.getName()) != null)
          continue;
		
		try {
			field.setAccessible(true);
        } catch (Throwable e) {
			e.printStackTrace();
        }
       
        fieldMap.put(field.getName(), field);
      }
    }

    return fieldMap;

从上面代码我们得出一个结论:对于同名字段fieldMap中存放的是子类field;
另外在序列化的时候,对于同名field是子类值在前,
父类值在后,于是在反序列化赋值的时候就变成:子类值->子类field, 父类值->子类field 所以就发生了值覆盖问题!


我们看一个例子:
public class FatherClass {

    private int intValue;

    public void setFatherValue(int intValue) {
        this.intValue = intValue;
    }

    public int getFatherValue() {
        return this.intValue;
    }
}

public class ChildClass extends FatherClass {

    private int intValue;

    public void setChildValue(int intValue) {
        this.intValue = intValue;
    }

    public int getChildrValue() {
        return this.intValue;
    }
}

然后我们写一下序列化代码:

ChildClass childClass = new ChildClass();
childClass.setFatherValue(2);
childClass.setChildValue(1);

OutputStream os = new FileOutputStream("hessianOutput");
AbstractHessianOutput out = new HessianOutput(os);
out.setSerializerFactory(new SerializerFactory());
out.writeObject(childClass);


下面是序列化结果:



这时候,我们反序列化一下:

InputStream in = new FileInputStream("hessianOutput");
AbstractHessianInput hessianInput = new HessianInput(in);
ChildClass childClass = (ChildClass) hessianInput.readObject();
System.out.println("ChildClassValue:" + childClass.getChildrValue());


朋友们可以先猜测一下,这里的输出结果是多少? 我想大家看了前面的分析一定能看出,这里的结果是2!

通过debug可以发现,intValue先被覆值成1,最后被赋值成2!

总结

1.JavaSerializer如何序列化一个自定义类;

2.自定义类的序列化格式:Mt 类名长度 具体类名 字段1 字段2 ... 结束标记z:

3.JavaSerialzer序列化存在的问题;
  • 大小: 10.7 KB
  • 大小: 43.4 KB
  • 大小: 120.6 KB
  • 大小: 44.4 KB
1
0
分享到:
评论

相关推荐

    hessian源码修改,包含了协议的修改

    公司搞soa治理,正好采用hessian的export暴露接口,我修改了下,客户端不需要采用url,直接采用servicename,从zookeeper取地址列表。客户端和服务端通过mina通讯,不采用原生的http协议。目前mina的编码解码还有待...

    Hessian多个版本打包下载

    这个压缩包包含了Hessian的多个版本,分别是Hessian3.1.6、Hessian3.2.1以及Hessian4.0.7。每个版本都有其特定的功能改进和优化,了解这些版本的区别和适用场景对于开发者来说至关重要。 Hessian3.1.6是Hessian的一...

    Hessian源码分析和Hack.doc

    《Hessian源码分析与Hack:携带远程调用端信息》 Hessian作为一种轻量级的RPC(远程过程调用)框架,因其高效、简洁的二进制协议,被广泛应用于构建Web服务。然而,在实际应用中,有时我们需要获取到远程调用端的IP...

    Hessian源码示例

    这个"**Hessian源码示例**"很可能包含了使用HessianCSharp库在C#环境中实现服务端和客户端通信的示例代码。HessianCSharp是.NET平台上的一个开源库,它实现了Hessian协议,使得开发者可以方便地在.NET应用程序之间...

    hessian学习基础篇——序列化和反序列化

    通过学习Hessian,我们可以更有效地处理数据传输,提高应用性能。 首先,让我们理解什么是序列化。序列化是将对象的状态转换为可存储或可传输的形式的过程。在Java中,如果一个类实现了Serializable接口,那么该类...

    Hessian的学习笔记

    五、Hessian的应用场景 Hessian可以应用于以下场景: * 远程过程调用(RPC):Hessian可以用于实现远程过程调用,例如远程调用服务端方法。 * 微服务架构:Hessian可以用于实现微服务架构中的服务调用。 * 分布式...

    hessian-4.0.38-sources.jar

    hessian源码

    Hessian 学习 例子 实例

    至于标签"源码",学习Hessian时查看源码有助于理解其内部工作原理,比如数据如何被编码和解码。你可以通过阅读开源的Hessian库,如Caucho Hessian的Java实现,来了解其底层细节。 至于"工具",有一些工具可以帮助...

    android实现Hessian通信源码

    在android与web端实现hessian通信,源码为android客户端源码,jar包来自http://hsrong.iteye.com/blog/1719996,google官网提供的hessdroid不可用,因为里面包含一些android不支持的类;hessian官网的jar包适用于web...

    hessian-3.2.0源码

    hessian-3.2.0源码,在做分布式交互时,可能要重写hessian

    Hessian-4.0.7(Jar包 + 源码)

    源码也使得开发者能够根据需要修改或扩展Hessian的功能,例如添加新的序列化策略,或者优化特定场景下的性能。 Hessian在实际应用中,通常被用来构建RPC(Remote Procedure Call)框架,使得服务端的方法可以直接在...

    Hessian学习简单demo

    Hessian是一种二进制协议,它被设计用于提高远程调用的...通过学习和实践这个Hessian demo,你将能够更好地应对涉及远程调用和数据交换的问题。无论是Java开发者还是C++开发者,Hessian都是值得了解和掌握的一项技术。

    Hessian

    在博客链接《iteye博客:550771》中,作者可能详细讨论了Hessian的使用方法、优缺点以及实战案例,可以作为学习Hessian的参考资料。 **六、Hessian的使用场景** 1. **微服务通信**:在微服务架构中,Hessian可以...

    Hessian学习入门实例

    这是一个Hessian入门学习的实例,程序包是一个web工程,使用intellij idea + Maven开发,其中通过两个方式展示了使用Hessian实现RMI的原理,一个是访问jsp页面,一个是在代码中手工创建client。对于Hessian Servlet...

    hessian学习实例

    在这个“hessian学习实例”中,我们将深入探讨Hessian框架的使用,特别是如何将其与Spring框架集成,以及如何构建server端和client端。 首先,让我们理解Hessian的工作原理。Hessian基于HTTP协议,它将Java对象序列...

    基于原生Hessian v4.0.51的SOFA-Hessian设计源码优化版

    该优化版SOFA-Hessian项目基于原生Hessian v4.0.51构建,经过蚂蚁金服多年内部稳定运行,现已进行多项改进。项目包含317个文件,其中Java源文件300个,XML、YML、Markdown、HTML等配置和说明文件共计7个,并支持...

    Hessian学习笔记

    10. **源码阅读**:对于深入理解Hessian的工作原理,阅读源码是一个很好的学习途径。通过查看源码,可以理解数据如何被序列化和反序列化,以及如何处理远程调用的细节。 综上所述,学习Hessian需要掌握二进制序列化...

    hessian最新源码分析.pdf

    在深入分析Hessian的源码之前,我们先来理解一下Hessian是什么。Hessian是一种二进制的Remoting协议,它允许Java应用通过HTTP进行高效、简洁的数据交换。Hessian客户端和服务端之间的通信基于HTTP协议,实现了远程...

    hessian3.0.1.jar

    hessian3.0.1.jar, 可以和spring1.x集成,不会报错

    hessian php与java通讯demo源码

    总之,这个"PHP与Java通信Demo源码"是一个很好的学习资源,帮助开发者理解如何在PHP和Java之间使用Hessian协议进行通信。通过分析和实践这些示例代码,你可以更好地掌握Hessian的工作原理和使用方式,为你的跨语言...

Global site tag (gtag.js) - Google Analytics