任何一个对象从一个JVM传输到另一个JVM,都要经过序列化为二进制数据(或者字符串等其他格式,比如JSON),然后在反序列化为Java对象,这最后都是通过二进制的数据在不同的JVM之间传输(一般是通过Socket和二进制的数据传输),本文定义一个比较符合工作中。
1. 定义三个POJO
Person类
package com.tom.hessian.common;
import java.io.Serializable;
import java.util.Date;
public class Person implements Serializable{
private int age;
private String name;
/**
* transient字段不会序列化
*/
private transient String sensitiveInformation;
private Date birthDay;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSensitiveInformation() {
return sensitiveInformation;
}
public void setSensitiveInformation(String sensitiveInformation) {
this.sensitiveInformation = sensitiveInformation;
}
public Date getBirthDay() {
return birthDay;
}
public void setBirthDay(Date birthDay) {
this.birthDay = birthDay;
}
}
Point类
package com.tom.hessian.common;
import java.io.Serializable;
public class Point implements Serializable{
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
ComplexModel
package com.tom.hessian.common;
import java.io.Serializable;
import java.util.List;
public class ComplexModel<T> implements Serializable{
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
private Integer id;
private Person person;
private List<T> points;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public List<T> getPoints() {
return points;
}
public void setPoints(List<T> points) {
this.points = points;
}
}
2. 服务接口
package com.tom.hessian.common;
public interface IComplexModelService {
//持久化
public void save(ComplexModel model);
//读取
public ComplexModel read(Integer modelId);
}
3. 服务器端代码
package com.tom.hessian.server;
import com.tom.hessian.common.ComplexModel;
import com.tom.hessian.common.IComplexModelService;
import java.util.HashMap;
import java.util.Map;
public class ComplexModelService implements IComplexModelService {
private Map<Integer,ComplexModel> models = new HashMap<Integer, ComplexModel>();
@Override
public void save(ComplexModel model) {
if (model.getId() == null){
throw new IllegalArgumentException("id could not be null");
}
models.put(model.getId(), model);
}
@Override
public ComplexModel read(Integer modelId) {
return models.get(modelId);
}
}
4. 客户端代码
package com.tom.hessian.client;
import com.caucho.hessian.client.HessianProxyFactory;
import com.tom.hessian.common.ComplexModel;
import com.tom.hessian.common.IComplexModelService;
import com.tom.hessian.common.Person;
import com.tom.hessian.common.Point;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class ComplextModelServiceTest {
public static void main(String[] args) throws Exception {
//RPC访问地址
String url = "http://localhost:8668/web/hessian_complex";
//接口的动态代理工厂
HessianProxyFactory factory = new HessianProxyFactory();
IComplexModelService service = (IComplexModelService) factory.create(IComplexModelService.class, url);
ComplexModel<Point> model = new ComplexModel<Point>();
model.setId(1);
Person person = new Person();
person.setName("Tom");
person.setAge(86);
person.setBirthDay(new Date());
person.setSensitiveInformation("This should be private over the wire");
model.setPerson(person);
List<Point> points = new ArrayList<Point>();
Point point = new Point();
point.setX(3);
point.setY(4);
points.add(point);
point = new Point();
point.setX(100);
point.setY(100);
points.add(point);
//远程方法调用
model.setPoints(points);
service.save(model);
model = service.read(model.getId());
List<Point> points1 = model.getPoints();
for(Point elem : points1) {
System.out.println(elem.getX() + "\t" + elem.getY());
}
}
}
5. web.xml配置服务
6. 运行
启动Jetty,运行ComplextModelTest的main方法,看到如下的异常,表示ComplexModel,Person以及Point都必须可序列化的(实现Serializable接口),
Exception in thread "main" java.lang.IllegalStateException: Serialized class com.tom.hessian.common.ComplexModel must implement java.io.Serializable
at com.caucho.hessian.io.SerializerFactory.getDefaultSerializer(SerializerFactory.java:353)
at com.caucho.hessian.io.SerializerFactory.loadSerializer(SerializerFactory.java:334)
at com.caucho.hessian.io.SerializerFactory.getSerializer(SerializerFactory.java:234)
at com.caucho.hessian.io.HessianOutput.writeObject(HessianOutput.java:322)
at com.caucho.hessian.io.HessianOutput.call(HessianOutput.java:132)
at com.caucho.hessian.client.HessianProxy.sendRequest(HessianProxy.java:293)
at com.caucho.hessian.client.HessianProxy.invoke(HessianProxy.java:171)
at com.sun.proxy.$Proxy0.save(Unknown Source)
at com.tom.hessian.client.ComplextModelServiceTest.main(ComplextModelServiceTest.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
7. 问题
对于范型的ComplexModel,Hessian是可以正确的序列化和反序列化的,这让我纳闷了,既然范型是编译时的行为,为什么序列化和反序列化能够成功?
我的理解:对于JVM而言,运行时没有范型信息,也就是说不管是否使用范型,它的字节码是完全一样的,当把一个没有范型信息的对象序列化为二进制数据,然后再把它反序列回来,它的数据就是反序列化前的样子。因此ComplexModel的List如果是Point类型的元素,那么反序列化后也是Point类型,这属于字节码级别的序列化和反序列化。反序化时为什么能够知道List元素的类型呢?原因是在序列化时,List中的每个元素的类型都会作为序列化的数据的一部分,这部分类型信息就是反序列化到正确类型的依据