介绍
Hessian是一个轻量级的remotingonhttp工具,使用简单的方法提供了RMI的功能。早在08年有人做过测试:一个UserData类,有一个字符串属性,一个日期属性,一个double属性,分别用java,hessian来序列化一百万次,结果让人吃惊,不止是hessian序列化的速度要比java的快上一倍,而且hessian序列化后的字节数也要比java的少一倍。
序列化
序列化宏观来看就四步
- 建立进行序列化的工厂类
- 用工厂类找到要要进行序列化类对应的序列化器
- 序列化器对将进行序列化的对象进行内省
- 内省完成后调用序列化器的writeObject(writeObject中按照一定规则把数据写入byte流)
下面用Hessian简单序列化一个对象来看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import com.caucho.hessian.io.HessianOutput; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable;
class User implements Serializable { String name; int age;
User() { age = 18; name = "cl0und"; } }
public class HessianTest { public static void main(String[] args) throws IOException { User user = new User(); ByteArrayOutputStream os=new ByteArrayOutputStream(); HessianOutput output=new HessianOutput(os); output.writeObject(user); } }
|
建立进行序列化的工厂类
把ByteArrayOutputStream放入了HessianOutput中典型的装饰器模式。
新建序列工厂类(后续会通过工厂类来寻找类的对应序列化器)。
工厂类的创建过程中,在类静态块中加入了java各种类型的反序列器以及和Hessian有关的反序列器。这里和序列化没关系,只是提一嘴。
在类初始化完成后,正式进入SerializerFactory的构造方法。可以看到SerializerFactory允许不安全的序列化,设置默认的上下文工厂类是_contextFactory,这个_contextFactory是生产序列化器的工厂。
用工厂类找到要要进行序列化类对应的序列化器
根据传入类型获取相应的序列化器。
获取序列化器的规则是如果_cachedSerializerMap里面又就直接从里面取,如果没有就用loadSerializer找,找出来的加入_cachedSerializerMap避免二次加载。
loadSerializer找的逻辑是先看有没有其它工厂类,如果有就用,如果没有就用_contextFactory造。
因为User是自建类型_contextFactory造不出对应序列化器。
下一步尝试getCustomSerializer来找序列化器,看方法的意思应该是找是否有客户自己定制的序列化器。生成序列器规则是:如果_customSerializerMap有就直接返回,如果没有就按照_类名+HessianSerializer_的规则来找。很遗憾我们并没有对User做个性化定制。
因为没有我们没有对User做个性化的序列化器,所以最后会调用getDefaultSerializer来返回默认序列化器。又因为User既不是内置类也不是和hessian有关的特殊类但是允许了不安全的序列化,所以最后被选中的序列化器是UnsafeSerializer。
序列化器对序列化对象进行内省
在构造方法中使用introspect进行内省。
introspect最后几步是在为类里面的属性找序列化器(getFiledSerializer)。
比如这里User类中name属性是String,就创建针对String的序列化器。
String的序列化器包含了field类及其偏移量。
创建类及及其字段序列化器后一路向外传,然后调用writeObject。
writeObjectBegin 写了Object的魔术头、长度、类名
writeObject10 负责序列化字段
先是写要序列化的字符串魔术值、字段名长度、字段名,其中 os.write(83)
代表 S
代表字符串。
String类型学序列化的时候
int类型序列化的时候
最后的结果
业务场景
环境搭建
说穿了就是RPC,用实际的代码来看看
pom.xml加入
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>4.0.60</version> </dependency> </dependencies>
|
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>HessianServlet</servlet-name> <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class> <init-param> <param-name>service-class</param-name> <param-value>org.syclover.hessian.BasicService</param-value> </init-param> </servlet>
<servlet-mapping> <servlet-name>HessianServlet</servlet-name> <url-pattern>/api/service</url-pattern> </servlet-mapping> </web-app>
|
代码忘记从网上哪里copy的了,侵删。
client/server端共有代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package org.syclover.hessian;
public interface IBasicApi {
public boolean setUserName(String name);
public String sayHello();
public User getUser();
}
|
client端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package org.syclover.hessian;
import com.caucho.hessian.client.HessianProxyFactory; import java.net.MalformedURLException;
public class ClientTest {
public static void main(String[] args) throws MalformedURLException {
String url = "http://localhost:8080/hessianTest_war_exploded/api/service"; HessianProxyFactory factory = new HessianProxyFactory(); IBasicApi api = (IBasicApi) factory.create(IBasicApi.class, url); api.setUserName("mahc"); System.out.println(api.sayHello()); System.out.println(api.getUser().getName()); System.out.println(api.getUser().getAge()); } }
|
server端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package org.syclover.hessian;
import org.syclover.hessian.User;
public class BasicService implements IBasicApi {
private String name;
@Override public boolean setUserName(String name) { this.name = name; return false; }
@Override public String sayHello() { return "Hello " + name + ",Welcome to Hessian!"; }
@Override public User getUser() { return new User(name, 23); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package org.syclover.hessian;
import java.io.Serializable;
public class User implements Serializable {
private String name; private Integer age;
public User() { super(); }
public User(String name, Integer age) { super(); this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; } }
|
这里为了测试方便把c/s放到一起了
进行RPC
参考
Hessian使用教程
JVM 类装载原理分析-ClassLoader原理分析
hessian 序列化实现 初探