1.Spring中除了提供HTTP调用器方式的远程调用,还对第三方的远程调用实现提供了支持,其中提供了对Hessian的支持。

Hessian是由Caocho公司发布的一个轻量级的二进制协议远程调用实现方案,Hessian也是基于HTTP协议的,其工作原理如下:

(1).客户端:

a.发送远程调用请求:

客户端程序—>发送远程调用请求—>Hessian客户端拦截器—>封装远程调用请求—>Hessian代理—>通过HTTP协议发送远程请求代理到服务端。

b.接收远程调用响应:

远程调用结果—>HTTP响应—>客户端。

(1).服务端:

a.接收远程调用请求:

远程调用HTTP请求—>HessianServiceExporter接收请求—>HessianExporter将远程调用对象封装为HessianSkeleton框架—> HessianSkeleton处理远程调用请求。

b.返回远程调用响应:

HessianSkeleton封装远程调用处理结果—>HTTP响应—>客户端。

本文章通过分析Spring对Hessian支持的相关源码,了解Spring对Hessian支持的具体实现。

2.Hessian的客户端配置:

Hessian的客户端需要做类似如下的配置:

[xhtml] view plaincopy

  1. <bean id=”hessianProxy” class=”org.springframework.remoting.caucho.HessianProxyFactoryBean”>

  2. <property name=”serviceUrl”>

  3. <value>http://hostAddress:8080/serviceUrlvalue>

  4. property>

  5. <property name=”serviceInterface”>

  6. <value>远程调用服务接口value>

  7. ]property>

  8. bean>

和HTTP调用器的配置类似,都需要配置远程调用请求的url,这个url要和服务端的url一致,Spring通过DispatcherServlet找到服务端对于的请求url。

HessianProxyFactoryBean是Spring中管理Hessian客户端的IoC容器,主要负责产生服务端远程调用代理和对客户端远程调用的拦截器设置。

3.HessianProxyFactoryBean:

HessianProxyFactoryBean生成远程调用代理和客户端远程调用拦截器设置,其源码如下:

[java] view plaincopy

public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean {

  • //远程调用代理对象

  • private Object serviceProxy;

  • //Spring IoC容器依赖注入完成后的回调方法

  • public void afterPropertiesSet() {

  • //首先调用父类HessianClientInterceptor的回调方法

  • super.afterPropertiesSet();

  • //创建远程调用代理对象并设置拦截器,注意这个this参数,因为//HessianProxyFactoryBean继承HessianClientInterceptor,因此其本身也

  • //是Hassien客户端拦截器

  • this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());

  • }

  • //Spring IoC容器的接口FactoryBean产生对象的方法,客户端通过该方法获取被管

  • //理的远程调用代理

  • public Object getObject() {

  • return this.serviceProxy;

  • }

  • //获取对象的类型

  • public Class getObjectType() {

  • return getServiceInterface();

  • }

  • //对象是否是单态类型,Spring默认管理的对象都是单态模式

  • public boolean isSingleton() {

  • return true;

  • }

  • }

HessianProxyFactoryBean最核心的功能就是在IoC容器回调方法中产生远程调用代理对象,在产生远程调用代理对象时,将代理对象的拦截器设置为其父类HessianClientInterceptor。

4.HessianClientInterceptor拦截客户端的远程调用请求:

HessianClientInterceptor对客户端的远程调用进行拦截,为客户端的远程调用创建Hessian代理,通过Hessian代理调用服务端远程调用对象,其源码如下:

[java] view plaincopy

  1. public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {

  2. //创建Hessiann代理工厂

  3. private HessianProxyFactory proxyFactory = new HessianProxyFactory();

  4. //Hessian代理

  5. private Object hessianProxy;

  6. //设置Hessian代理工厂

  7. public void setProxyFactory(HessianProxyFactory proxyFactory) {

  8. this.proxyFactory = (proxyFactory != null ? proxyFactory : new HessianProxyFactory());

  9. }

  10. //设置Hessian序列化工厂

  11. public void setSerializerFactory(SerializerFactory serializerFactory) {

  12. this.proxyFactory.setSerializerFactory(serializerFactory);

  13. }

  14. //设置Hessian是否发送java集合类型对象

  15. public void setSendCollectionType(boolean sendCollectionType) {

  16. this.proxyFactory.getSerializerFactory().setSendCollectionType(sendCollectionType);

  17. }

  18. //设置远程调用时是否重载方法

  19. public void setOverloadEnabled(boolean overloadEnabled) {

  20. this.proxyFactory.setOverloadEnabled(overloadEnabled);

  21. }

  22. //设置远程调用用户名

  23. public void setUsername(String username) {

  24. this.proxyFactory.setUser(username);

  25. }

  26. //设置远程调用密码

  27. public void setPassword(String password) {

  28. this.proxyFactory.setPassword(password);

  29. }

  30. //设置是否使用Hessian的Debug调试模式

  31. public void setDebug(boolean debug) {

  32. this.proxyFactory.setDebug(debug);

  33. }

  34. //设置是否使用chunked端口发送Hessian请求

  35. public void setChunkedPost(boolean chunkedPost) {

  36. this.proxyFactory.setChunkedPost(chunkedPost);

  37. }

  38. //设置Hessian等待响应的超时时长

  39. public void setReadTimeout(long timeout) {

  40. this.proxyFactory.setReadTimeout(timeout);

  41. }

  42. //设置是否使用Hessain版本2协议解析请求和响应

  43. public void setHessian2(boolean hessian2) {

  44. this.proxyFactory.setHessian2Request(hessian2);

  45. this.proxyFactory.setHessian2Reply(hessian2);

  46. }

  47. //设置是否使用Hessian版本2协议解析请求

  48. public void setHessian2Request(boolean hessian2) {

  49. this.proxyFactory.setHessian2Request(hessian2);

  50. }

  51. //设置是否使用Hessian版本2协议解析响应

  52. public void setHessian2Reply(boolean hessian2) {

  53. this.proxyFactory.setHessian2Reply(hessian2);

  54. }

  55. //子类HessianProxyFactoryBean的回调方法调用此回调方法

  56. public void afterPropertiesSet() {

  57. //调用其父类UrlBasedRemoteAccessor的回调方法获取客户端配置的请求url

  58. super.afterPropertiesSet();

  59. //初始化Hessian代理

  60. prepare();

  61. }

  62. //初始化Hessian代理

  63. public void prepare() throws RemoteLookupFailureException {

  64. try {

  65. //创建Hessian代理

  66. this.hessianProxy = createHessianProxy(this.proxyFactory);

  67. }

  68. catch (MalformedURLException ex) {

  69. throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);

  70. }

  71. }

  72. //创建Hessian代理

  73. protected Object createHessianProxy(HessianProxyFactory proxyFactory) throws MalformedURLException {

  74. Assert.notNull(getServiceInterface(), "'serviceInterface' is required");

  75. //使用Hessian代理工厂创建Hessian代理

  76. return proxyFactory.create(getServiceInterface(), getServiceUrl());

  77. }

  78. //拦截器客户端请求的方法

  79. public Object invoke(MethodInvocation invocation) throws Throwable {

  80. if (this.hessianProxy == null) {

  81. throw new IllegalStateException("HessianClientInterceptor is not properly initialized - " +

  82. "invoke 'prepare' before attempting any operations");

  83. }

  84. //获取当前环境中线程类加载器

  85. ClassLoader originalClassLoader = overrideThreadContextClassLoader();

  86. try {

  87. //调用Hessian代理的方法,是Hessian远程调用的入口方法,使用JDK反射机制

  88. return invocation.getMethod().invoke(this.hessianProxy, invocation.getArguments());

  89. }

  90. //处理Hessian远程调用中的异常

  91. catch (InvocationTargetException ex) {

  92. Throwable targetEx = ex.getTargetException();

  93. if (targetEx instanceof InvocationTargetException) {

  94. targetEx = ((InvocationTargetException) targetEx).getTargetException();

  95. }

  96. if (targetEx instanceof HessianConnectionException) {

  97. throw convertHessianAccessException(targetEx);

  98. }

  99. else if (targetEx instanceof HessianException || targetEx instanceof HessianRuntimeException) {

  100. Throwable cause = targetEx.getCause();

  101. throw convertHessianAccessException(cause != null ? cause : targetEx);

  102. }

  103. else if (targetEx instanceof UndeclaredThrowableException) {

  104. UndeclaredThrowableException utex = (UndeclaredThrowableException) targetEx;

  105. throw convertHessianAccessException(utex.getUndeclaredThrowable());

  106. }

  107. else {

  108. throw targetEx;

  109. }

  110. }

  111. catch (Throwable ex) {

  112. throw new RemoteProxyFailureException(

  113. "Failed to invoke Hessian proxy for remote service [" + getServiceUrl() + "]", ex);

  114. }

  115. //重置类加载器

  116. finally {

  117. resetThreadContextClassLoader(originalClassLoader);

  118. }

  119. }

  120. //将Hessian异常转换为Spring远程调用异常

  121. protected RemoteAccessException convertHessianAccessException(Throwable ex) {

  122. if (ex instanceof HessianConnectionException || ex instanceof ConnectException) {

  123. return new RemoteConnectFailureException(

  124. "Cannot connect to Hessian remote service at [" + getServiceUrl() + "]", ex);

  125. }

  126. else {

  127. return new RemoteAccessException(

  128. "Cannot access Hessian remote service at [" + getServiceUrl() + "]", ex);

  129. }

  130. }

  131. }

通过上面对HessianClientInterceptor的源码分析,我们可以看到Hessian客户端拦截器提供的最重要的方法是对远程调用拦截的方法invoke,在该方法中使用JDK的反射机制调用Hessian代理对象的指定方法。而Hessian代理是由Hessain代理器工厂HessianProxyFactory产生的,这个Hessian代理器工厂是有Hessian提供的。

5.Hessian服务器端配置:

在Hessian的服务端需要进行类似如下的配置:

[xhtml] view plaincopy

  1. <bean id=”/serviceUrl” class=”org.springframework.remoting.caucho.HessianServiceExporter”>

  2. <property name=”service”>

  3. <ref bean=”service”/>

  4. property>

  5. <property name=”serviceInterface”>

  6. <value>远程服务接口value>

  7. property>

  8. bean>

Spring的HessianServiceExporter把远程调用服务整合到Spring MVC框架中,通过DispatcherServlet将客户端请求转发到服务器端相应的请求url远程对象上。

注意:serviceUrl要和客户端serviceUrl中配置的相同,service即是服务端提供服务的远程对象。

6.HessianServiceExporter处理Hessian远程调用请求:

HessianServiceExporter接收客户端的远程调用请求,并调用HessianExporter具体处理远程调用,并且远程调用结果封装到HTTP响应中返回,e家装网源码如下:

[java] view plaincopy

  1. public class HessianServiceExporter extends HessianExporter implements HttpRequestHandler {

  2. //处理Hessian请求,并将处理结果封装为Hessian响应返回

  3. public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

  4. //Hessian只支持HTTP的POST方法

  5. if (!"POST".equals(request.getMethod())) {

  6. throw new HttpRequestMethodNotSupportedException(request.getMethod(),

  7. new String[] {"POST"}, "HessianServiceExporter only supports POST requests");

  8. }

  9. //设置Hessian响应内容类型为:application/x-hessian

  10. response.setContentType(CONTENT_TYPE_HESSIAN);

  11. try {

  12. //HessianExporter真正处理Hessian请求和封装Hessian响应的方法

  13. invoke(request.getInputStream(), response.getOutputStream());

  14. }

  15. catch (Throwable ex) {

  16. throw new NestedServletException("Hessian skeleton invocation failed", ex);

  17. }

  18. }

  19. }

7.HessianExporter处理Hessian请求并将结果封装为HTTP响应:

[java] view plaincopy

  1. public class HessianExporter extends RemoteExporter implements InitializingBean {

  2. //Hessian HTTP响应内容类型

  3. public static final String CONTENT_TYPE_HESSIAN = "application/x-hessian";

  4. //Hessian序列化工厂

  5. private SerializerFactory serializerFactory = new SerializerFactory();

  6. //Hessian Debug日志

  7. private Log debugLogger;

  8. //Hessian服务端框架

  9. private HessianSkeleton skeleton;

  10. //设置Hessian序列化工厂

  11. public void setSerializerFactory(SerializerFactory serializerFactory) {

  12. this.serializerFactory = (serializerFactory != null ? serializerFactory : new SerializerFactory());

  13. }

  14. //设置序列化集合发送java集合类型

  15. public void setSendCollectionType(boolean sendCollectionType) {

  16. this.serializerFactory.setSendCollectionType(sendCollectionType);

  17. }

  18. //设置Hessian调试模式

  19. public void setDebug(boolean debug) {

  20. this.debugLogger = (debug ? logger : null);

  21. }

  22. //回调方法

  23. public void afterPropertiesSet() {

  24. prepare();

  25. }

  26. //初始化Hessian服务框架

  27. public void prepare() {

  28. //这里调用父类RemoteExporter的方法检查服务提供类、服务接口

  29. checkService();

  30. checkServiceInterface();

  31. //创建远程服务类的Hessian框架

  32. this.skeleton = new HessianSkeleton(getProxyForService(), getServiceInterface());

  33. }

  34. //远程调用处理入口

  35. public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {

  36. Assert.notNull(this.skeleton, "Hessian exporter has not been initialized");

  37. doInvoke(this.skeleton, inputStream, outputStream);

  38. }

  39. //远程调用处理方法

  40. protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)

  41. throws Throwable {

  42. //获取类加载器

  43. ClassLoader originalClassLoader = overrideThreadContextClassLoader();

  44. try {

  45. InputStream isToUse = inputStream;

  46. OutputStream osToUse = outputStream;

  47. //设置Hessian调试日志

  48. if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) {

  49. PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger));

  50. HessianDebugInputStream dis = new HessianDebugInputStream(inputStream, debugWriter);

  51. dis.startTop2();

  52. HessianDebugOutputStream dos = new HessianDebugOutputStream(outputStream, debugWriter);

  53. dos.startTop2();

  54. isToUse = dis;

  55. osToUse = dos;

  56. }

  57. if (!isToUse.markSupported()) {

  58. isToUse = new BufferedInputStream(isToUse);

  59. isToUse.mark(1);

  60. }

  61. int code = isToUse.read();

  62. int major;

  63. int minor;

  64. AbstractHessianInput in;

  65. AbstractHessianOutput out;

  66. //根据客户端不同的Hessian版本,设置不同的Hessian抽象输入/输出

  67. //Hessian2.0

  68. if (code == 'H') {

  69. major = isToUse.read();

  70. minor = isToUse.read();

  71. if (major != 0x02) {

  72. throw new IOException("Version " + major + "." + minor + " is not understood");

  73. }

  74. in = new Hessian2Input(isToUse);

  75. out = new Hessian2Output(osToUse);

  76. in.readCall();

  77. }

  78. //Hessian2.0

  79. else if (code == 'C') {

  80. isToUse.reset();

  81. in = new Hessian2Input(isToUse);

  82. out = new Hessian2Output(osToUse);

  83. in.readCall();

  84. }

  85. //Hessian1.0

  86. else if (code == 'c') {

  87. major = isToUse.read();

  88. minor = isToUse.read();

  89. in = new HessianInput(isToUse);

  90. if (major >= 2) {

  91. out = new Hessian2Output(osToUse);

  92. }

  93. else {

  94. out = new HessianOutput(osToUse);

  95. }

  96. }

  97. else {

  98. throw new IOException("Expected 'H'/'C' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code);

  99. }

  100. //设置Hessian序列化工厂

  101. if (this.serializerFactory != null) {

  102. in.setSerializerFactory(this.serializerFactory);

  103. out.setSerializerFactory(this.serializerFactory);

  104. }

  105. try {

  106. //通过服务端远程对象的Hessian框架处理远程调用

  107. skeleton.invoke(in, out);

  108. }

  109. finally {

  110. try {

  111. in.close();

  112. isToUse.close();

  113. }

  114. catch (IOException ex) {

  115. }

  116. try {

  117. out.close();

  118. osToUse.close();

  119. }

  120. catch (IOException ex) {

  121. }

  122. }

  123. }

  124. //重置类加载器

  125. finally {

  126. resetThreadContextClassLoader(originalClassLoader);

  127. }

  128. }

  129. }

通过上面对HessianExporter源码分析,我们看到真正进行远程调用处理的是由Hessian提供的服务端HessianSkeleton。

Hessian怎样实现远程调用的更多相关文章

  1. Hessian轻量级二进制远程调用框架

    Hessian轻量级二进制远程调用框架 Hessian是一个轻量级的二进制远程调用框架,官方文档地址,它主要包括Hessian远程调用协议.Hessian序列化协议以及客户端服务端代理等几部分,关于H ...

  2. [转载] 基于Dubbo的Hessian协议实现远程调用

    转载自http://shiyanjun.cn/archives/349.html Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行 ...

  3. 基于Dubbo的Hessian协议实现远程调用

    Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行远程调用,也就是说,服务调用方需要使用Java语言来基于Dubbo调用提供方服务, ...

  4. Spring远程调用技术<2>-Hessian和Burlap

    上篇谈到RMI技术,加上Spring的封装,用起来很方便,但也有一些限制 这里的Hessian和Burlap解决了上篇提到的限制,因为他们是基于http的轻量级远程服务. Hessian,和RMI一样 ...

  5. Hessian——轻量级远程调用方案

    Hessian是caucho公司开发的一种基于二进制RPC协议(Remote Procedure Call protocol)的轻量级远程调用框架.具有多种语言的实现,但用的最多的当然是Java实现 ...

  6. 远程调用之RMI、Hessian、Burlap、Httpinvoker、WebService的比较

    一.综述 本文比较了RMI.Hessian.Burlap.Httpinvoker.WebService5这种通讯协议的在不同的数据结构和不同数据量时的传输性能. RMI是java语言本身提供的远程通讯 ...

  7. 远程调用——hessian使用入门

    Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单.快捷.采用的是二进制RPC协议,因为采用的是二进制协 ...

  8. [转]Hessian——轻量级远程调用方案

    Hessian是caucho公司开发的一种基于二进制RPC协议(Remote Procedure Call protocol)的轻量级远程调用框架.具有多种语言的实现,但用的最多的当然是Java实现 ...

  9. Java 远程调用之Hessian简例

    1. [代码]1.服务接口(Hello.java) package server; public interface Hello { String hello(String name);}2. [代码 ...

随机推荐

  1. Page-encoding specified in XML prolog (UTF-8) is different from that specified in page directive (utf-8)

    org.apache.jasper.JasperException:xxx.jsp(1,1) Page-encoding specified in XML prolog (UTF-8) is diff ...

  2. Shell 编程基础之 && 与 ||

    一.引言 Shell 在执行某个命令的时候,会返回一个返回值,该返回值保存在 shell 变量 $? 中.当 $? == 0 时,表示执行成功:当 $? == 1 时,表示执行失败.有时候,下一条命令 ...

  3. 2016中国大学生程序设计竞赛 - 网络选拔赛 C. Magic boy Bi Luo with his excited tree

    Magic boy Bi Luo with his excited tree Problem Description Bi Luo is a magic boy, he also has a migi ...

  4. python 列表转换成字符串

    用字符串的 join 方法: li = ['a', 'b', 'c', 'd'] s = ''.join(li) print(s) abcd 用字符串的占位符替换 li = ['a', 'b', 'c ...

  5. ffmpeg(2.6) rockplayer android 下编译 小记.

    最近因为一些需求,开始学习 ffmgeg 在android 上使用. 使用的环境: 1,VMware V8 虚似机 安装的 FedoraV18 系统.(下载地址,请baidu),虚似机,最好有20-3 ...

  6. 随鼠标轮动翻动层————jquery小练习

    闲来无事在网站上看见一个网页制作的不错,就仿照做来看看.特此记录下来. 亮点:随鼠标上下滚动,展示页面随之不同,翻动效果. 功能点:鼠标向上,向下判断事件. css 代码 html { overflo ...

  7. 【转载】Linux 信号列表

    转自:http://blog.csdn.net/muge0913/article/details/7322710 信号及其简介 信号是一种进程通信的方法,他应用于异步事件的处理.信号的实现是一种软中断 ...

  8. jQuery的封装方式与JS中new的实现原理

    function jQuery() { return new jQuery.fn.init(); } jQuery.fn = jQuery.prototype = { init: function() ...

  9. Reg于Wire的不同点

    Reg 比喻为“相机” Reg型,是always中被赋值的信号,往往代表触发器,但不一定是触发器 Wire 比喻为“镜子” Wrie型,assign指定的组合逻辑的信号 好好想想 ,还是能有个大致的概 ...

  10. C++ 画星号图形——圆形(核心代码记录)

    void Circle::print_char(int x1,int x2) { int i; // 计算这一行的宽度,终端最多显示80列 ; n = n > ? : n; ; i < n ...