Spring使用Hessian实现远程调用

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/serviceUrl</value>
  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

  1. public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object> {
  2. //远程调用代理对象
  3. private Object serviceProxy;
  4. //Spring IoC容器依赖注入完成后的回调方法
  5. public void afterPropertiesSet() {
  6. //首先调用父类HessianClientInterceptor的回调方法
  7. super.afterPropertiesSet();
  8. //创建远程调用代理对象并设置拦截器,注意这个this参数,因为//HessianProxyFactoryBean继承HessianClientInterceptor,因此其本身也
  9. //是Hassien客户端拦截器
  10. this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
  11. }
  12. //Spring IoC容器的接口FactoryBean产生对象的方法,客户端通过该方法获取被管
  13. //理的远程调用代理
  14. public Object getObject() {
  15. return this.serviceProxy;
  16. }
  17. //获取对象的类型
  18. public Class<?> getObjectType() {
  19. return getServiceInterface();
  20. }
  21. //对象是否是单态类型,Spring默认管理的对象都是单态模式
  22. public boolean isSingleton() {
  23. return true;
  24. }
  25. }

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响应中返回,源码如下:

[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。

时间: 2024-07-31 14:33:44

Spring使用Hessian实现远程调用的相关文章

spring代理模式 service远程调用,插件执行

最近,研究了一下平台远程调用的过程,和service层插件执行的原理,记录一下. 1.远程service调用过程 首先看一下类的继承结构 封装调用处理过程 封装service调用接口 封装service请求信息 封装请求信息处理类 先描述一个调用过程: 平台在controller里面获取service的时候都是通过lookup方式(就是用了spring查找service的bean对象的代理对象),远程的service配到配置文件里面,所以在lookup的时候,如果配置文件中有的service,返

dubbo_实现Hessian的远程调用协议

1.优点 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:Hessian二进制序列化 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件. 适用场景:页面传输,文件传输,或与原生hessian服务互操作 约束: 参数及返回值需实现Serializable接口 参数及返回值不能自定义实现List, Map, Number, Date, Calendar等接口,只能用JDK自带的实现,因为hessian会做特殊处理,自定义实现类中的属

bos 第5天(定区的添加、定区的分页查询、hessian远程调用实现获取客户信息)

BOS项目笔记 第5天 今天内容安排: 1.添加定区功能 2.定区分页查询 3.hessian入门----远程调用技术 4.基于hessian实现定区关联客户 1. 添加定区 定区可以将取派员.分区.客户信息关联到一起. 页面:WEB-INF/pages/base/decidedzone.jsp 第一步:使用下拉框展示取派员数据,修改combobox的URL地址,发送请求 第二步:在StaffAction中提供listajax方法,查询没有作废的取派员,返回json数据 第三步:在StaffSe

Spring远程调用技术&lt;3&gt;-Spring的HTTP Invoker

前面提到RMI使用java标准的对象序列化机制,但是很难穿透防火墙.  另一方面,Hessian和Burlap能很好地穿透防火墙,但是使用私有的对象序列化机制. Spring提供的http invker是一个新的远程调用模型,作为Spring框架的一部分,能够执行基于HTTP的远程调用(让防火墙不为难),并使用java的序列化机制(让开发者也乐观其变). Spring的HTTPinvoker把HTTP的简单性和java内置的对象序列化机制融合在一起.这使得HTTPinvoker成为替代RMI H

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

转载自http://shiyanjun.cn/archives/349.html Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行远程调用,也就是说,服务调用方需要使用Java语言来基于Dubbo调用提供方服务,限制了服务调用方.同时,使用Dubbo的Hessian协议实现提供方服务,而调用方可以使用标准的Hessian接口来调用,原生的Hessian协议已经支持多语言客户端调用,支持语言如下所示: Java:http://h

Spring远程调用HTTP invoker

前言 在日常的工作中,会有客户端调用远程服务端接口的需求,这样的需求实现,在网上查到有像RMI.hessian.Burlap等,下文给出HTTP Invoker实现远程调用,他不用使用者考虑对象序列化的问题,对使用者,非常的友好. 下面介绍在Spring中配置HTTP Invoker实现远程调用.分为服务提供方和服务消费方两部分. 案例 服务提供方 1.定义一个接口和接口实现类,服务提供方提供. WelcomeService.java package com.aaron.service; imp

Hessian怎样实现远程调用

1.Spring中除了提供HTTP调用器方式的远程调用,还对第三方的远程调用实现提供了支持,其中提供了对Hessian的支持. Hessian是由Caocho公司发布的一个轻量级的二进制协议远程调用实现方案,Hessian也是基于HTTP协议的,其工作原理如下: (1).客户端: a.发送远程调用请求: 客户端程序—>发送远程调用请求—>Hessian客户端拦截器—>封装远程调用请求—>Hessian代理—>通过HTTP协议发送远程请求代理到服务端. b.接收远程调用响应:

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

Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行远程调用,也就是说,服务调用方需要使用Java语言来基于Dubbo调用提供方服务,限制了服务调用方.同时,使用Dubbo的Hessian协议实现提供方服务,而调用方可以使用标准的Hessian接口来调用,原生的Hessian协议已经支持多语言客户端调用,支持语言如下所示: Java:http://hessian.caucho.com/#Java Flash/Flex:http:/

【spring源码学习】spring的远程调用实现源码分析

[一]spring的远程调用提供的基础类 (1)org.springframework.remoting.support.RemotingSupport ===>spring提供实现的远程调用客户端实现的基础类 ===>例子:org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean org.springframework.remoting.caucho.HessianProxyFactoryBean (2)org.