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. [Liferay6.2.2]AUI的小坑:input的type属性

    <aui:input name="name" label="姓名" value="<%=student.getName() %>&q ...

  2. jquery:closest和parents的主要区别

    closest和parents的主要区别是:1,前者从当前元素开始匹配寻找,后者从父元素开始匹配寻找:2,前者逐级向上查找,直到发现匹配的元素后就停止了,后者一直向上查找直到根元素,然后把这些元素放进 ...

  3. NIO的一些相关链接

    Architecture of a Highly Scalable NIO-Based Server Scalable IO in Java Tricks and Tips with NIO part ...

  4. 一个print的简单错误

    原因是python 2.X 可以print 内容 而3.X 取消了这种写法 改成了print(内容)

  5. Codeforces Round B. Buttons

    Manao is trying to open a rather challenging lock. The lock has n buttons on it and to open it, you ...

  6. an excellent capability of C# language and compiler

    Sometimes you want to write code that works for different primitive types, and as C# doesn't support ...

  7. [转载]Grunt插件之LiveReload 实现页面自动刷新,所见即所得编辑

    配置文件下载  http://vdisk.weibo.com/s/DOlfks4wpIj LiveReload安装前的准备工作: 安装Node.js和Grunt,如果第一次接触,可以参考:Window ...

  8. 【Eclipse】 Eclipse 中JPEGEncodeParam 错误波浪线问题

    [异常信息] Description Resource Path Location Type Access restriction: The method encode(BufferedImage, ...

  9. ZeroMQ接口函数之 :zmq_ctx_get - 得到环境上下文的属性

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_ctx_get zmq_ctx_get(3)  ØMQ Manual - ØMQ/3.2.5 Name zmq_c ...

  10. About_Web

    成功网站的三要诀:内容.设计.营销 内容为王: 高质量的内容会促使网站走向成功.首先,用户有需求,他们需要被感动,被娱乐,被有料的内容和产品所吸引.漂亮的背景和亮骚的特效可能会有所助益,但终究只是辅助 ...