Hessian学习总结:
最近学习了一下hessian,发现用hessian来实现远程调用确实很简单易用。hessian序列化采用了标准的二进制格式来实现,可以完成跨语言通讯,可以为搭建异构系统提供支持。hessian的传输协议采用的是http。
hessian 是面向接口的,通过接口来暴露具体的服务。
实现一个hessian的Demo:
Client端的代码(客户端只需要这么一段代码即可):
public class RomoteTest {
/**
* @param args
*/
public static void main(String[] args) {
Stringurl = "http://127.0.0.1:8080/HessianStudy/hessianService" ;
HessianProxyFactoryfactory = new HessianProxyFactory();
try{
Hellohello = (Hello)factory.create(Hello.class ,url);
System. out.println(hello.hello( "jianying"));
} catch(MalformedURLException e){
e.printStackTrace();
}
}
}
server端代码:
接口定义:
public interface Hello {
Stringhello(String name);
}
服务的实现:
public class HelloImpl implements Hello {
@Override
public String hello(String name) {
return "hello,"+name+ "!欢迎学习Hessian" ;
}
}
服务端部署描述符的配置:
<servlet>
<servlet-name >hessianService </servlet-name >
<servlet-class >com.caucho.hessian.server.HessianServlet </servlet-class >
<init-param >
<param-name >service-class </param-name >
<param-value >server.impl.HelloImpl </param-value >
</init-param >
<!-- 这个的作用是表示启动时加载 这里的1代表的是加载的优先级,数字越小优先级别越高 -->
<load-on-startup >1 </load-on-startup >
</servlet >
<servlet-mapping >
<servlet-name >hessianService </servlet-name >
<url-pattern >/hessianService </url-pattern >
</servlet-mapping >
将我们自己定义的服务接口作为启动参数传给hessian的servlet即可。
环境的配置嘛就是,客户端和服务端都下载hessian的jar包即可,我做这个实现用的是,
然后服务器用的是tomcat7。
将上面这个demo跑起来还是很容易的,我看网络上的blog也都是从搭建应用的层面来写的,下面我就从hessian的源代码的层面来解读一下hessian吧。
hessian的源代码学习:
看客户端的这个调用:
因为hessian是基于接口提供服务的,这个demo的调用首先是基于java的动态代理机制(InvocationHandler,Proxy)来创建了一个基于Hello这个接口的代理类。其实通过这个工厂的类名就可以看出来。factory.create 这个函数看下去源码是这段:
很经典的用java api实现的动态代理的创建。那么要看到这个调用是怎么来完成的,那当然就要看一下这个Handler的 invoke方法的实现了。 invoke方法很长就不贴出来了,他调用的的sendRequest函数是实现网络通信的关键。。。
invoke方法中调用到了这么一个函数,通信的操作也都是在这个函数中完成的,首先第一个画红框的部分是根据url获取了一个和服务端通信的连接。当我调试到第二个画红框的地方的时候阻塞掉了,可见通信是发出去的,并且阻塞来等待网络IO的返回。那我们继续跟进看一下 HessianConnection 的这个被阻塞掉的函数,发现这个Connection里面调用了HttpURLConnection。此函数的相关代码如下:
这步调用了HttpURLConnection的 getResponseCode() 方法。HttpURLConnection 是java.net包下的类。进入getResponseCode() 函数继续看:
阻塞发生在这里,这里就是等待网络IO返回真正发生阻塞的地方。这里真正调用的是 sun.net.www.protocol.http.HttpURLConnection的getInputStream类它继承了java.net.HttpURLConnection 继续进入这个getInputStream代码,发现:
这个函数首先是将请求写出,然后再用httpClient来解析返回的信息。这就难怪我们最初的哪个函数的名字叫做sendRequest了。 在继续跟进writeRequest函数,发现里面做了一些校验及进行了一些头信息的设置之外就是调用自己的成员变量
将请求发出。httpClient的内容等有时间在分析了。网络IO返回解析掉response之后,我们的HessainURLConnection当中封装的HttpURLConnection就拿到了们的返回信息。之后回到我们的HessianProxy的invoke方法。
只贴一部分哈,想要说的意思就是拿到返回之后,通过Hessian的内置协议将返回内容还原掉,将远程调用的结果拿到返回。有兴趣大家可以自己看一下HessianProxy的invoke方法。以上就是Hessian client端的主要流程。 下面我们再来看一下服务端的代码吧。
Hessian Server端:
Server端的代码当然就是要从部署描述符 web.xml的这个 servlet看起了,进入这个类的源码吧。。。
我们主要看两个函数,一个是servlet加载的时候的初始化方法 init以及对外提供服务的service方法。
这部加载了我们配置的service-class,可见这个service-class被写死了,如果在web.xml里配置其他的param-name 看来还是不行的。另外在这个init方法里面还看到了 home-class以及home-api的获取,这个我理解为相对于service-class的另外一种配置,home-class代表具体的实现,home-api代表接口的定义,我们通过这个配置我们服务端具体的服务类也是可以的。另外还有api-class,这些应该就是服务类的不同配置方式。
HessianServlet的有个成员变量:
服务端的调用方法基本都是封装在了 这个 Skeleton里面。hessian的序列化传输协议也是封装在这里的。 这两个Skeleton定义了不同参数配置的不同的实现方式,就是上面提到的 home-class等不同的配置方式。我们在再来看下这个Servlet的service方法吧。
这个方法到时很清爽的,可见大部分的操作都被封装在了这个invoke方法里面。
这个invoke就是调用了 Skeleton的方法。需要我们传入基本信息接口完成,可见封装的还是蛮好的。 那么再来看一下这个Skeleton的invoke方法
首先第一个红框是读出了 从网络Io输入流中读出 header。这个header是什么呢?并且这个header直接通过下面的开关语句创建了 HessianInput和HessianOutput
刚开始我有点疑惑不解,但是看到了createHessianInput里面的代码后发现,这个是用来定义不同版本的。
CALL_1_REPLY_1:的意思就是调用端用的是1.0版本,并且返回1.0版本,剩下的以此类推,这个是个版本兼容操作。HESSIAN_2就是都是用2.0版本。 成功创建了HessianInput 和HessianOutput就继续向下调用,
我们继续看这个更加底层的invoke方法:
更下层的就是根据我们注册的服务实现类,通过反射来调用我们注册的方法。最后将result通过HessianOutput 写出去
当然hessian内部协议的东西就是封装在这个out里的,这里就先不解读了。