Hessian的使用以及理解

  • Hessian版本:3.1.5

将包括如下的内容:

  1. Hessian的基本使用
  2. Hessian的原理
  3. Hessian和Spring 的结合使用
  4. 扩展

简单说来,Hessian是一个轻量级的RPC框架(RPC是什么?请参考这里)。

它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较友好。

但是它的参数和返回值都需要实现Serializable接口。


简单实现一个Hessian的例子:

  1. 1.创建接口和实现类
public interface Basic {String sayHello(String name);
}
public class BasicImpl implements Basic{public String sayHello(String name) {return "This is Hello words from HESSIAN Server. " + name;}
}
  1. 2.配置HessianServlet, web.xml中:
  <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>example.impl.BasicImpl</param-value></init-param></servlet><servlet-mapping><servlet-name>HessianServlet</servlet-name><url-pattern>/hessian</url-pattern></servlet-mapping>

我们将会把Servlet部署在Tomcat上,端口8080。

  1. 3.编写客户端代码:
public class BasicClient {public static void main(String[] args) {try {String url = "http://localhost:8080/hessian";HessianProxyFactory factory = new HessianProxyFactory();factory.setOverloadEnabled(true);Basic basic = (Basic) factory.create(Basic.class, url);System.out.println(basic.sayHello("SW"));}catch (Exception e){e.printStackTrace();}}

创建HessianProxyFactory对象,创建Basic “对象”,然后调用sayHello()方法。

整个过程感觉很简单,并没有什么配置。

启动Tomcat,运行Client。

输出如下:

This is Hello words from HESSIAN Server. SW

可见是调用成功了。


等等,这个过程到底发生了些什么?

我们先从服务端说起,主要是有这几个步骤:

  • 编写接口和实现类
  • 在web.xml中声明HessianServlet,并且将上一步的实现类设置为Servlet的[service-class]属性值
  • 将Servlet部署在Tomcat容器中

可见我们所有的工作都围绕在HessianServlet在展开。该Servlet中有两个比较重要的方法:init()、service();

init方法初始化服务和服务对象,主要分为3步:

通过home-class或者service-class创建服务端的实现类实例;

if (_homeImpl != null) {}else if (getInitParameter("home-class") != null) {String className = getInitParameter("home-class");Class homeClass = loadClass(className);_homeImpl = homeClass.newInstance();init(_homeImpl);}else if (getInitParameter("service-class") != null) {String className = getInitParameter("service-class");Class homeClass = loadClass(className);_homeImpl = homeClass.newInstance();init(_homeImpl);}else {if (getClass().equals(HessianServlet.class))throw new ServletException("server must extend HessianServlet");_homeImpl = this;}

通过home-api或者api-class加载实现类的接口对象;

      if (_homeAPI != null) {}else if (getInitParameter("home-api") != null) {String className = getInitParameter("home-api");_homeAPI = loadClass(className);}else if (getInitParameter("api-class") != null) {String className = getInitParameter("api-class");_homeAPI = loadClass(className);}else if (_homeImpl != null)_homeAPI = _homeImpl.getClass();

init方法还会创建HessianSkeleton对象,这是Hessian服务端的核心功能部分。

HessianSkeleton继承自AbstractSkeleton,其构造方法,将会从实现类中抽取方法和方法的Method对象,并且存储到_methodMap中。

对于一个Servlet来说其service方法是对外提供服务的方法:

/*** Execute a request.  The path-info of the request selects the bean.* Once the bean's selected, it will be applied.*/public void service(ServletRequest request, ServletResponse response)throws IOException, ServletException{HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse res = (HttpServletResponse) response;if (! req.getMethod().equals("POST")) {res.setStatus(500, "Hessian Requires POST");PrintWriter out = res.getWriter();res.setContentType("text/html");out.println("<h1>Hessian Requires POST</h1>");return;}String serviceId = req.getPathInfo();String objectId = req.getParameter("id");if (objectId == null)objectId = req.getParameter("ejbid");ServiceContext.begin(req, serviceId, objectId);try {InputStream is = request.getInputStream();OutputStream os = response.getOutputStream();HessianInput in = new HessianInput(is);HessianOutput out = new HessianOutput(os);if (objectId != null)_objectSkeleton.invoke(in, out);else_homeSkeleton.invoke(in, out);} catch (RuntimeException e) {throw e;} catch (ServletException e) {throw e;} catch (Throwable e) {throw new ServletException(e);} finally {ServiceContext.end();}}

最主要的是调用HessianSkeleton对象的invoke方法。注意,Servlet实例中有两个HessianSkeleton变量,分别是:_objectSkeleton和 _homeSkeleton,调用谁,是由objectid决定的。此处还有不明白的地方

invoke方法:

首先从HessianInput对象中获取到Method信息,获取到真正的service对象。

根据反射机制,调用service对象的invoke方法,获取到返回值。

最后调用HessianOutput对象将结果写回到调用方。


客户端代码

Hessian原生API编写客户端HessianClient:

public class BasicClient {public static void main(String[] args) {try {String url = "http://localhost:8080/hessian";HessianProxyFactory factory = new HessianProxyFactory();factory.setOverloadEnabled(true);Basic basic = (Basic) factory.create(Basic.class, url);System.out.println(basic.sayHello("SW"));}catch (Exception e){e.printStackTrace();}}
}

创建HessianProxyFacotry,创建接口Basic的代理对象,然后调用sayHello()方法。

那么重点就在于创建代理对象,首先创建HessianProxyFacotry对象,构造方法中创建了一个HessianProxyResolver对象,这个对象的lookup方法将用来查找远程服务。此外HessianProxyFacotry还有包括权限验证方面的支持。

创建了factory之后,接下来就是通过Class对象和远程服务的URL创建代理对象了。

HessianProxyFactory使用HessianProxy对象作为代理的Handler,也就是说,我们对代理对象的所有操作,都会由这个handler来处理。handler的invoke方法,在进行一些方法名和参数的确认之后,创建HttpURLConnection对象,调用sendRequest方法,将方法名和参数用HessianOutput对象(设置序列化的方式)的call方法,写入到服务端。

主要代码如下:

protected URLConnection sendRequest(String methodName, Object []args)throws IOException{URLConnection conn = null;conn = _factory.openConnection(_url);// Used chunked mode when available, i.e. JDK 1.5.if (_factory.isChunkedPost() && conn instanceof HttpURLConnection) {try {HttpURLConnection httpConn = (HttpURLConnection) conn;httpConn.setChunkedStreamingMode(8 * 1024);} catch (Throwable e) {}}addRequestHeaders(conn);OutputStream os = null;try {os = conn.getOutputStream();} catch (Exception e) {throw new HessianRuntimeException(e);}try {if (log.isLoggable(Level.FINEST)) {PrintWriter dbg = new PrintWriter(new LogWriter(log));os = new HessianDebugOutputStream(os, dbg);}AbstractHessianOutput out = _factory.getHessianOutput(os);out.call(methodName, args);out.flush();return conn;} catch (IOException e) {if (conn instanceof HttpURLConnection)((HttpURLConnection) conn).disconnect();throw e;} catch (RuntimeException e) {if (conn instanceof HttpURLConnection)((HttpURLConnection) conn).disconnect();throw e;}}

服务端拿到请求,进行反序列化,然后将方法调用,再将结果序列化之后写回到connection。所以,客户端在sendRequest之后,所要做的就是将返回的结果进行解析,看返回的code是不是200:

conn = sendRequest(mangleName, args);if (conn instanceof HttpURLConnection) {httpConn = (HttpURLConnection) conn;int code = 500;try {code = httpConn.getResponseCode();} catch (Exception e) {}parseResponseHeaders(conn);if (code != 200) {StringBuffer sb = new StringBuffer();int ch;.....AbstractHessianInput in = _factory.getHessianInput(is);in.startReply();Object value = in.readObject(method.getReturnType());if (value instanceof InputStream) {value = new ResultInputStream(httpConn, is, in, (InputStream) value);is = null;httpConn = null;}elsein.completeReply();return value;

解析HessianInput对象,并且从中读取到结果返回。

至此,服务端和客户端的交互过程已经简单地介绍完毕。


Spring也为Hessian提供了很友好的支持,通过使用spring-remoting包,我们可以很方便地发布和调用服务。

这部分提供一个简单的实现例子:

在web.xml中,我们配置SpringMVC的DispatcherServlet:

<servlet><servlet-name>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>SpringMVC</servlet-name><url-pattern>/remote/*</url-pattern></servlet-mapping>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.1.xsd"default-lazy-init="true"><bean id = "basicService" class="example.impl.BasicImpl"/><bean name="/basicHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"><property name="service" ref="basicService"/><property name="serviceInterface" value="example.Basic"/></bean>
</beans>

这里,我们使用了org.springframework.remoting.caucho.HessianServiceExporter来发布服务。将程序部署在tomcat中。

客户端,使用org.springframework.remoting.caucho.HessianProxyFactoryBean来代理请求:

client.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.1.xsd"default-lazy-init="true"><bean id="basicService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"><property name="serviceUrl" value="http://localhost:8080/remote/basicHessianService"/><property name="serviceInterface" value="example.Basic"/></bean>
</beans>

编写客户端:

public class SpringClient {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:client.xml"});Basic basic = (Basic)context.getBean("basicService");System.out.println(basic.sayHello("SUNWEI"));}
}

这样,服务端/客户端的代码都已经编写完成。


最原始的实现,我们的服务是通过Servlet来绑定的,而Spring的实现,我们使用了SpringMVC的加载时机,将配置文件加载。HessianServiceExporter

public class HessianServiceExporter extends RemoteExporter implements HttpRequestHandler, InitializingBean {....

这个类实现了InitializingBean接口,这是spring-beans包中很重要的一个扩展接口。

这个接口的说明如下:

Interface to be implemented by beans that need to react once all their
properties have been set by a BeanFactory: for example, to perform custom
initialization, or merely to check that all mandatory properties have been set.

也就是说,它会随着Spring容器(此处为Spring MVC容器)的启动而被加载。看看HessianServiceExporter的实现:

public void prepare() {HessianSkeleton skeleton = null;try {try {Constructor ctor = (class$com$caucho$hessian$server$HessianSkeleton == null?(class$com$caucho$hessian$server$HessianSkeleton = class$("com.caucho.hessian.server.HessianSkeleton")):class$com$caucho$hessian$server$HessianSkeleton).getConstructor(new Class[]{class$java$lang$Object == null?(class$java$lang$Object = class$("java.lang.Object")):class$java$lang$Object, class$java$lang$Class == null?(class$java$lang$Class = class$("java.lang.Class")):class$java$lang$Class});this.checkService();this.checkServiceInterface();skeleton = (HessianSkeleton)ctor.newInstance(new Object[]{this.getProxyForService(), this.getServiceInterface()});} catch (NoSuchMethodException var4) {Constructor ctor = (class$com$caucho$hessian$server$HessianSkeleton == null?(class$com$caucho$hessian$server$HessianSkeleton = class$("com.caucho.hessian.server.HessianSkeleton")):class$com$caucho$hessian$server$HessianSkeleton).getConstructor(new Class[]{class$java$lang$Object == null?(class$java$lang$Object = class$("java.lang.Object")):class$java$lang$Object});skeleton = (HessianSkeleton)ctor.newInstance(new Object[]{this.getProxyForService()});}} catch (Throwable var5) {throw new BeanInitializationException("Hessian skeleton initialization failed", var5);}if(hessian2Available) {this.skeletonInvoker = new Hessian2SkeletonInvoker(skeleton, this.serializerFactory);} else {this.skeletonInvoker = new Hessian1SkeletonInvoker(skeleton, this.serializerFactory);}}public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {Assert.notNull(this.skeletonInvoker, "HessianServiceExporter has not been initialized");if(!"POST".equals(request.getMethod())) {throw new HttpRequestMethodNotSupportedException("POST", "HessianServiceExporter only supports POST requests");} else {try {this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());} catch (Throwable var4) {throw new NestedServletException("Hessian skeleton invocation failed", var4);}}}

在prepare方法中,获取service和serviceInterface的配置,创建HessianSkeleton对象。
同时,还实现了HttpRequestHandler,spring-web中的接口。
又因为实现了HttpRequestHandler接口,所以在handleRequest方法中,可以像HessianServlet的service方法一样,调用Hessian2SkeletonInvoker的invoke方法进行实际的方法调用。


最后一点尾巴

定义一个自己的HttpRequestHandler对象,配置在applicationContext.xml中,然后通过页面访问:

public class MyHandler implements HttpRequestHandler, InitializingBean {public void afterPropertiesSet() throws Exception {System.out.println("初始化 MyHandler");}public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("执行 MyHandler");}
}

配置在applicationContext.xml中:

<bean id = "/myHandler" class="client.MyHandler"/>

通过Spring MVC的上下文加载该Handler,启动Tomcat的时候,可以看到控制台输出:

初始化 MyHandler

在浏览器中访问:http://localhost:8080/remote/myHandler

将触发执行:执行 MyHandler

-EOF-

转自:https://blog.csdn.net/sunwei_pyw/article/details/74002351

转载于:https://www.cnblogs.com/linshuqin/p/10155926.html

Hessian的使用以及理解(转)相关推荐

  1. Hessian的使用以及理解

    Hessian的使用以及理解 Hessian版本:3.1.5 将包括如下的内容: Hessian的基本使用 Hessian的原理 Hessian和Spring 的结合使用 扩展 简单说来,Hessia ...

  2. MsgPack的浅浅理解

    前言 记得18年3月刚刚入职的时候,企业邮箱收到的第一个事故报告就是说在bean中添加字段没有加在字段序的最后导致线上出问题,我只是知其然不知其所以然.于是趁着过年没有紧急任务,查阅一些资料充充电. ...

  3. python 逻辑回归 复杂抽样_逻辑回归(Logistic Regression) ----转载

    概要: 1. 介绍Logistic Regression的数学模型,推导并详细解释求解最优回归系数的过程: 2. Python实现Logistic Regression的基本版: 3. 介绍sklea ...

  4. Numerical Optimization Ch17. Penalty and Augmented Lagragian Methods

    第十七章: 惩罚函数法与增广Lagrange函数法 文章目录 第十七章: 惩罚函数法与增广Lagrange函数法 1. 二次罚函数法 1.1 动机 1.2 算法框架 1.3 二次罚函数法的收敛性 1. ...

  5. 资料搜集-JAVA系统的梳理知识17- RPC

    ## 什么是 RPC?RPC原理是什么?### **什么是 RPC?**RPC(Remote Procedure Call)-远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层 ...

  6. Jacobian矩阵和Hessian矩阵的理解

    深度学习中梯度向量的计算,Jacobian矩阵和Hessian矩阵是基础的知识点. 求微分其实就是线性化,导数其实就是线性空间之间的线性变换,Jaocibian矩阵本质上就是导数. 比如,映射在处的导 ...

  7. 如何理解神经网络优化中Momentem能够缓解hessian矩阵病态的问题

    如何理解神经网络优化中Momentem能够缓解hessian矩阵病态的问题? 1.首先介绍一下,矩阵的病态问题 矩阵病态主要是因为矩阵向量之间相关性太大,在二维上说就是矩阵向量之间的夹角太小,导致这两 ...

  8. 通俗理解Hessian矩阵的几何意义

    先用一个不精确的通俗的例子来说明: ①初中数学里面的f(x)=12Ax2−bx+cf(x)=\frac{1}{2}Ax^2-bx+cf(x)=21​Ax2−bx+c 显然我们知道A>0时,f(x ...

  9. Hessian源码分析(java)

    个人博客: 戳我,戳我 先扯一扯 前一篇博文Hessian通信案例(java)简单实现了Java版的Hessian客户端和服务端的通信,总体看来,实现起来比较简单,整个基于Hessian的远程调用过程 ...

最新文章

  1. 删除 此电脑 文件夹
  2. C#winform使用+=和-=订阅事件和移除事件订阅
  3. Mysql Federated Server 示例
  4. goroutine并发扫描MySQL表_goroutine 并发之搜索文件内容
  5. 更新显示当前歌曲的名称 winform 0130
  6. 协方差矩阵经线性变化可以变成不相关的
  7. 如何判断真假“中台”?
  8. 用CSS制作细线表格
  9. win10u盘被写保护怎么解除_教你win10系统中u盘被写保护怎么解除
  10. 服务器上运行程序提示*工作已停止,已停止工作 出现了一个问题,导致程序停止正常工作。请关闭该程序...
  11. 第一阶段✦第一章☞信息化知识
  12. 前端常用PS技巧总结之更换图片背景图片
  13. 微软Office 2013:会受企业待见的最佳新功能
  14. 《算法谜题》-第二章 谜题
  15. GDUT_排位赛题解报告_第3场_B.Loan Repayment
  16. Rasa课程、Rasa培训、Rasa面试、Rasa实战系列之Finding Unexpected Intents
  17. Java 计算时间差
  18. c语言编程文本文件的写入,c语言 文本文件的操作 字符写入
  19. javaweb企业员工考勤管理系统案例
  20. windows下安装speedtest的经历和问题

热门文章

  1. Cloud Foundry 登录 Azure,一键快速部署 PaaS
  2. 第二章:搭建Android开发环境(读后感)
  3. (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  4. 配置tomcat支持ssl
  5. Bootstrap3.0学习第九轮(CSS补充)
  6. Xen的network-bridge模式
  7. oracle数据库修改写入状态,【学习笔记】Oracle oradebug 使用oradebug修改数据库SCN方法案例...
  8. MJExtension 模型嵌套模型数组
  9. PSVR开发者需要了解的9件事
  10. 中国批准AMD收购赛灵思!苏妈花350亿美元集齐CPU\GPU\FPGA三大芯片业务