Hessian 是一个rpc框架, 我们需要先写一个服务端, 然后在客户端远程的调用它即可。

服务端:

服务端通常和spring 做集成。

首先写一个接口:

public interface HelloService {    
void sayHello(String name);
}

然后一个实现,实现使用@Service("helloService")   实现spring bean注册。

@Service("helloService")    
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello " + name + "!");
}
} spring xml配置中,通过org.springframework.remoting.caucho.HessianServiceExporter 完成服务的暴露:
<!-- Name保持与web.xml中的一致,web.xml下文中描述 -->
<bean name="HelloServiceExporter"
class="org.springframework.remoting.caucho.HessianServiceExporter">
<!-- service的ref与HelloServiceImpl中@Service中配置的一致 -->
<property name="service" ref="helloService" />
<!-- 接口的路径 -->
<property name="serviceInterface"
value="hessian.HelloService" />
</bean>
web.xml 的关键配置:
<servlet>
<!-- servlet-name保持与spring-hessian.xml中一致 -->
<servlet-name>HelloServiceExporter</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServiceExporter</servlet-name>
<url-pattern>/HelloService</url-pattern>
</servlet-mapping>
HessianServiceExporter 有两个关键的属性: serviceInterface 和 service 
serviceInterface 是必须是一个接口,也就是服务,值必须是一个接口的全路径FQN
service 是具体的实现bean,是一个实例的引用
HttpRequestHandlerServlet extends HttpServlet
HttpRequestHandlerServlet 的关键代码是:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
LocaleContextHolder.setLocale(request.getLocale()); try {
this.target.handleRequest(request, response);
} catch (HttpRequestMethodNotSupportedException var8) {
String[] supportedMethods = var8.getSupportedMethods();
if (supportedMethods != null) {
response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
} response.sendError(405, var8.getMessage());
} finally {
LocaleContextHolder.resetLocaleContext();
} } 其实就是调用了 HessianServiceExporter 的handler 方法,HessianServiceExporter 的关键代码是:
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (!"POST".equals(request.getMethod())) {
throw new HttpRequestMethodNotSupportedException(request.getMethod(), new String[]{"POST"}, "HessianServiceExporter only supports POST requests");
} else {
response.setContentType("application/x-hessian"); try {
this.invoke(request.getInputStream(), response.getOutputStream());
} catch (Throwable var4) {
throw new NestedServletException("Hessian skeleton invocation failed", var4);
}
}
} 代码关系是:

HessianServiceExporter extends HessianExporter implements HttpRequestHandler
HessianExporter extends RemoteExporter implements InitializingBean

HessianExporter has a HessianSkeleton
HessianSkeleton extends AbstractSkeleton

HessianSkeleton 的invoke 方法实现关键的request处理

具体来说:

HessianInput extends AbstractHessianInput
HessianOutput extends AbstractHessianOutput

HessianInput 有提供 readHeader、readMethod、readMethodArgLength、readBytes、readInt、readLong、readString/readDouble/readObject、 readCall、 startCall、completeCall、readReply、startReply、completeReply 等方法

HessianOutput 有writeBytes Int long , Double,String, Object, writeReply 等方法

req 获取 InputStream, res 获取OutputStream。

HessianInput 从 req 中读取 方法,参数, 然后method 反射调用 service, 然后通过 HessianOutput 写回结果

in.completeCall();
out.writeReply(result); // 发送处理结果到 远程客户端
out.close(); 这样, 就完成了服务端的编写,看起来还是比较简单的。
客户端:

因为Hessian服务端暴露的服务实际上是一个基于http实现通信的。 故我们需要通过http 调用 Hessian。 客户端可以是一个web应用,也可以是一个简单的main: 方式1,通过HessianProxyFactory:
public static void main(String[] args) {    
try {
String url = "http://localhost:8080/HelloService";
HessianProxyFactory factory = new HessianProxyFactory();
HelloService helloService = (HelloService) factory.create(
HelloService.class, url);
helloService.sayHello("张三");
} catch (Exception e) {
e.printStackTrace();
}
}
稍微查看一下源码,我们就会发现:

HessianProxy implements InvocationHandler, Serializable 可见 hessian 是使用jdk 动态代理实现的, 故我们需要一个接口

HessianURLConnection extends AbstractHessianConnection implements HessianConnection

HessianURLConnection 有一个 java.net.URLConnection  , 可见Hessian 的通信主要就是通过http, 具体是 URLConnection实现的。

HessianProxy has a HessianProxyFactory
HessianProxyFactory has a HessianConnectionFactory
HessianProxyFactory 用来获取conn: conn = this._factory.getConnectionFactory().open(this._url);
HessianProxy relate to HessianConnection

具体来说是这样的:

create 返回的是一个代理, return Proxy.newProxyInstance(loader, new Class[]{api, HessianRemoteObject.class}, handler);

HessianProxy 的invoke 是关键:

sendRequest 方法
os = conn.getOutputStream(); 通过conn 返回OutputStream

OutputStream os 强制转换为 AbstractHessianOutput, 然后

out.call(methodName, args); // 发送数据到 远程服务端
out.flush();
conn.sendRequest(); // 返回一个statusMessage 
is = httpConn.getInputStream(); // 通过conn获取InputStream,

conn = this.sendRequest(mangleName, args);
is = conn.getInputStream(); // 通过sendRequest发送完数据后, 就可以通过conn 获取远程的返回的数据了。

InputStream的is 转换为 AbstractHessianInput 后, 然后就可以 readObject
value = in.readObject(method.getReturnType()); // readObject 最终返回了 远程调用的结果。 至此,单次 rpc 结束

方式2,通过HessianProxyFactoryBean:
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-servlet.xml");
HelloService ser = (HelloService) classPathXmlApplicationContext.getBean("testHessianService");
ser.sayHello("lk AA");
} 其实也就是把前面的HessianProxyFactory 集成到了spring,封装成了bean

HessianProxy implements InvocationHandler, Serializable 可见 hessian 是使用jdk 动态代理实现的, 故我们需要一个接口

HessianURLConnection extends AbstractHessianConnection implements HessianConnection

HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object>

HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor

HessianClientInterceptor has a HessianProxyFactory

HessianClientInterceptor 定义hessianProxy:有一个属性: private Class serviceInterface;
serviceInterface 定义serviceInterface:有一个属性: private Class serviceInterface;
UrlBasedRemoteAccessor 定义serviceUrl:有一个属性: private String serviceUrl;

hessianProxy 是通过proxyFactory(也就是HessianProxyFactory)创建, 可见, HessianProxyFactoryBean 还是通过HessianProxyFactory来完成的主要工作的。

简单说,其实就是 spring 通过反射的调用proxyFactory 的远程方法。

当然,实际使用的时候, 我们不会使用 ClassPathXmlApplicationContext, 它仅仅是在测试环境中使用。

												

Hessian 源码简单分析的更多相关文章

  1. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  2. FFmpeg源码简单分析:libswscale的sws_scale()

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  3. Django-session中间件源码简单分析

    Django-session中间件源码简单分析 settings里有关中间件的配置 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddlew ...

  4. FFmpeg源码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源码分析文章列表: [架构图] FFmpeg源码结构图 - 解码 FFm ...

  5. negroni-gzip源码简单分析解读

    negroni-gzip源码简单分析解读 这是一个为Negroni设计的gzip压缩处理中间件,需要用到已有的compress中的gzip,阅读了不长的源码之后,总结了一些关键要点和注意点. 检查是否 ...

  6. FFmpeg的HEVC解码器源码简单分析:概述

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  7. FFmpeg的HEVC解码器源码简单分析:解码器主干部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  8. urllib源码简单分析

    对下面这段代码做分析 import urllib params = urllib.urlencode({'wd': 'python'}) f = urllib.urlopen("http:/ ...

  9. CardboardSDK-iOS 源码简单分析

    该项目地址: 地址 克隆地址为 https://github.com/rsanchezsaez/CardboardSDK-iOS.git 目前如果想在iOS设备上实现双目VR的功能,Google 已经 ...

随机推荐

  1. webdriver 启动chrome时加载配置

    Selenium操作浏览器是不加载任何配置的,网上找了半天,关于Firefox加载配置的多点,Chrome资料很少,下面是关于加载Chrome配置的方法:  一.加载所有Chrome配置 用Chrom ...

  2. C++进阶--静态初始化的惨败

    /* Initialization Fiasco 一个会使程序崩溃的细微的问题 */ // 不同文件的编译顺序是不确定的 // 如果一个文件依赖另一个文件的对象先初始化,可能出现问题 // 解决方法: ...

  3. Memcached在.NET应用程序中的使用

    在应用程序运行的过程中总会有一些经常需要访问并且变化不频繁的数据,如果每次获取这些数据都需要从数据库或者外部文件系统中去读取,性能肯定会受 到影响,所以通常的做法就是将这部分数据缓存起来,只要数据没有 ...

  4. 25天javaweb基础

    第一天(html) 表格标签,超链接标签,图片标签,排版标签,列表标签 第二天(css) 表单标签 第三天(JS) js语法 定时器(系统对象的定时器setinterval,js的定时器seTimeo ...

  5. 学习笔记之Intermediate Python for Data Science | DataCamp

    Intermediate Python for Data Science | DataCamp https://www.datacamp.com/courses/intermediate-python ...

  6. 计划任务at、crontab

    at一次性计划任务 格式: at + 时间 命令 安装at # yum install at -y 如果执行at命令时,出现一下情况 Can't open /var/run/atd.pid to si ...

  7. Vue + TypeScript + ElementUI 封装表头查询组件

    前段时间有朋友私信我 Vue + TypeScript 的问题,然后就打算写一篇 Vue + TypeScript 封装组件的文章 正好公司项目中需要封装一个表头查询组件,就拿出来分享一下~ 组件的整 ...

  8. JDK、JRE与JVM的关系

  9. [UE4]动态改变相机OrthWidh、关掉阴影

    在制作缩略图的时候,每次都要手动不断尝试合适的OrthWidh,很是麻烦. 一.使用蓝图实现鼠标滚动动态改变OrthWidh.Get Ortho Width,Set Ortho Width 二.去掉阴 ...

  10. [UE4]时间轴线TimeLine,Lerp插值

    一.TimeLine时间轴线 勾选“User Last Keyframe”表示使用时间轴最后一个关键帧所在时间点作为结束时间,而不是使用设置的5秒作为结束时间点. 二.Lerp插值 Lerp插值一般与 ...