读书人

Hessian 的字段序列化初记

发布时间: 2012-08-24 10:00:20 作者: rapoo

Hessian 的字段序列化小记

?

Hessian 的字段序列化小记

?

一背景:

今天线上碰到一个问题,在通过hessian的反序列化的时候失败了。

简单查看了下原因,是因为服务端和客户端依赖的bean包版本不一致导致的。

?

二 具体分析:

Client --- > commons-entity?

Sever ?--- ?> commons-entity?

?

做了依赖倒置处理,服务端及客户端都持有服务接口及对象的包。

?

commons-entity 存在两个版本:服务端版本(新版本)及客户端版本(旧版本)。两个版本之间区别在于对于某个字段的类型做了改变。

?

下面就分析下不同场景下hessian的处理结果。

?

1.服务端 字段A:Date ? ? ? 客户端 字段A:Timestamp ?反序列化出错

2.服务端 字段A:Timestamp ?客户端 字段A:Date ? ? ? 序列化正常

?

因为Date是Timestamp的父类,故在反序列化时,向上转型是没有问题的。反之向下转型就存在问题。所以1就会出问题,因为1需要将Date转化成Timestamp类型。

?

结论:在反序列化时,若是客户端对象是服务端对象的父类,那么不会失败。

?

tip:对于日期类型的对象还是建议定义为long类型。

?

三 扩展

以上就是我们具体的故障情况,那么扩展的来测试下一下两种情况是否存在问题。

?

场景一:

服务端 存在字段A ? ? ? 客户端 无字段A ? ?反序列化正常

?

结论:反序列化时依赖于客户端所持有的对象,对服务传递字节流进行处理的。不是依赖于服务端的对象处理。这样也很容易理解。因为是客户端需要生成对象。

?

场景二:

服务端 无字段A ? ? ? ? 客户端 存在字段A ?反序列化正常

?

结论:反序列化是先解析客户端对象,然后拿着该对象的属性到服务端的字节流中查找,若是存在则生成,若是不存在则忽略该属性的设置,设置为默认值。

?

?

总结:在客户端反序列化时候,从服务端中取,若是取不到,则不管,设置为默认值。不管服务端属性是否多余客户端,一切以客户端为准。

?

四 具体实践代码

?

附一个hessian的简单使用的核心代码:

?

?

server:package com.inter12.hessian;import org.mortbay.jetty.Server;import org.mortbay.jetty.servlet.Context;import org.mortbay.jetty.servlet.ServletHolder;import com.inter12.hessian.service.IPersonServiceServlet;/** * Hello world! */public class App {    /**     *      * URL: http://localhost:8080/IPersonService     * @param args     * @throws Exception     */    public static void main(String[] args) throws Exception {                System.out.println("Hello World!");        // 1.启动一个SERVER 并指定端口        Server server = new Server(8080);        // 2.设置应用的跟路径为 / 你也可以设置成自己需要的应用名字。例如我这个的应用是/jTest        Context context = new Context(server, "/", Context.SESSIONS);        // 3.添加servlet. OKServlet就是我们具体处理业务的类!        context.addServlet(new ServletHolder(new IPersonServiceServlet()), "/IPersonService");        // 4.启动。收工,这样就搞定了内置 servlet容器!        server.start();            }}
?

?

IPersonServiceServlet的实现:

?

?

public class IPersonServiceServlet extends HessianServlet implements IPersonService {    @Override    public void service(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {        super.service(arg0, arg1);    }    @Override    public void hello() {    }    @Override    public IPerson gerPerson() {        Person person = new Person();        person.setAge(20);        person.setName("joan");        person.setBirthDay(new Timestamp(System.currentTimeMillis()));        return person;    }}
?

?

相关的依赖:

<dependency>

<groupId>org.mortbay.jetty</groupId><artifactId>jetty</artifactId><version>6.1H.14</version></dependency><dependency><groupId>com.inter12.hesian</groupId><artifactId>common-interface</artifactId><version>1.0.0</version></dependency><dependency><groupId>hessian</groupId><artifactId>hessian</artifactId><version>3.0.1</version></dependency>
?

?

?

Client:

?

public class App {    public static void main( String[] args ) throws MalformedURLException    {//        System.out.println( "Hello World!" );                String url = "http://localhost:8080/IPersonService";                HessianProxyFactory factory = new HessianProxyFactory();        IPersonService service = (IPersonService)factory.create(IPersonService.class, url);        Person person = (Person)service.gerPerson();        System.out.println(person.show());                    }}
?

相关的依赖:

?

?

<dependency><groupId>hessian</groupId><artifactId>hessian</artifactId><version>3.0.1</version></dependency><dependency><groupId>com.inter12.hesian</groupId><artifactId>common-interface</artifactId><version>1.0.0</version></dependency>
?

?

五 写在最后

先占个坑,以后再深挖hessian的协议。




java序列化类:JavaSerializer的源码:

private static FieldSerializer getFieldSerializer(Class type){
......
else if (java.util.Date.class.equals(type)
|| java.sql.Date.class.equals(type)
|| java.sql.Timestamp.class.equals(type)
|| java.sql.Time.class.equals(type)) {
return DateFieldSerializer.SER;
}
......
}

其中DateFieldSerializer.SER的源码为:
static class DateFieldSerializer extends FieldSerializer {
static final FieldSerializer SER = new DateFieldSerializer();

void serialize(AbstractHessianOutput out, Object obj, Field field)
throws IOException
{
java.util.Date value = null;

try {
value = (java.util.Date) field.get(obj);
} catch (IllegalAccessException e) {
log.log(Level.FINE, e.toString(), e);
}

if (value == null)
out.writeNull();
else
out.writeUTCDate(value.getTime());
}
}

综上:我理解是java在序列化的时候把时间类型都转化为java.util.Date类型在进行序列化传输。

Java反序列化类:JavaDeserializer源码似乎是支持Date-->Timestamp的
static class SqlDateFieldDeserializer extends FieldDeserializer {
.....

java.util.Date date = (java.util.Date) in.readObject();
value = new java.sql.Date(date.getTime());
.....


}

static class SqlTimestampFieldDeserializer extends FieldDeserializer {
.......
java.util.Date date = (java.util.Date) in.readObject();
value = new java.sql.Timestamp(date.getTime());
........


}
综上,java反序列化的时候可以把,先构造成java.util.Date类,然后再转化为不同的类型如java.sql.Date和java.sql.Timestamp等等


薛少,你看看源码,是不是原因还没有找到呀?你那抛出的异常也应该贴出来的。搞了一下午总算有点回复了。为了和你交流也不容易呀!哈哈
2 楼 aliahhqcheng 2012-06-12

java序列化类:JavaSerializer的源码:

private static FieldSerializer getFieldSerializer(Class type){
......
else if (java.util.Date.class.equals(type)
|| java.sql.Date.class.equals(type)
|| java.sql.Timestamp.class.equals(type)
|| java.sql.Time.class.equals(type)) {
return DateFieldSerializer.SER;
}
......
}

其中DateFieldSerializer.SER的源码为:
static class DateFieldSerializer extends FieldSerializer {
static final FieldSerializer SER = new DateFieldSerializer();

void serialize(AbstractHessianOutput out, Object obj, Field field)
throws IOException
{
java.util.Date value = null;

try {
value = (java.util.Date) field.get(obj);
} catch (IllegalAccessException e) {
log.log(Level.FINE, e.toString(), e);
}

if (value == null)
out.writeNull();
else
out.writeUTCDate(value.getTime());
}
}

综上:我理解是java在序列化的时候把时间类型都转化为java.util.Date类型在进行序列化传输。
[/color][/size]
Java反序列化类:JavaDeserializer源码似乎是支持Date-->Timestamp的
static class SqlDateFieldDeserializer extends FieldDeserializer {
.....

java.util.Date date = (java.util.Date) in.readObject();
value = new java.sql.Date(date.getTime());
.....


}

static class SqlTimestampFieldDeserializer extends FieldDeserializer {
.......
java.util.Date date = (java.util.Date) in.readObject();
value = new java.sql.Timestamp(date.getTime());
........


}
[color=red]综上,java反序列化的时候可以把,先构造成java.util.Date类,然后再转化为不同的类型如java.sql.Date和java.sql.Timestamp等等[size=x-small]


薛少,你看看源码,是不是原因还没有找到呀?你那抛出的异常也应该贴出来的。搞了一下午总算有点回复了。为了和你交流也不容易呀!哈哈
3 楼 inter12 2012-06-15 哈哈,明天我仔细看下,好久没来了!
aliahhqcheng 写道

java序列化类:JavaSerializer的源码:

private static FieldSerializer getFieldSerializer(Class type){
......
else if (java.util.Date.class.equals(type)
|| java.sql.Date.class.equals(type)
|| java.sql.Timestamp.class.equals(type)
|| java.sql.Time.class.equals(type)) {
return DateFieldSerializer.SER;
}
......
}

其中DateFieldSerializer.SER的源码为:
static class DateFieldSerializer extends FieldSerializer {
static final FieldSerializer SER = new DateFieldSerializer();

void serialize(AbstractHessianOutput out, Object obj, Field field)
throws IOException
{
java.util.Date value = null;

try {
value = (java.util.Date) field.get(obj);
} catch (IllegalAccessException e) {
log.log(Level.FINE, e.toString(), e);
}

if (value == null)
out.writeNull();
else
out.writeUTCDate(value.getTime());
}
}

综上:我理解是java在序列化的时候把时间类型都转化为java.util.Date类型在进行序列化传输。
[/color][/size]
Java反序列化类:JavaDeserializer源码似乎是支持Date-->Timestamp的
static class SqlDateFieldDeserializer extends FieldDeserializer {
.....

java.util.Date date = (java.util.Date) in.readObject();
value = new java.sql.Date(date.getTime());
.....


}

static class SqlTimestampFieldDeserializer extends FieldDeserializer {
.......
java.util.Date date = (java.util.Date) in.readObject();
value = new java.sql.Timestamp(date.getTime());
........


}
[color=red]综上,java反序列化的时候可以把,先构造成java.util.Date类,然后再转化为不同的类型如java.sql.Date和java.sql.Timestamp等等[size=x-small]


薛少,你看看源码,是不是原因还没有找到呀?你那抛出的异常也应该贴出来的。搞了一下午总算有点回复了。为了和你交流也不容易呀!哈哈

读书人网 >编程

热点推荐