Hessian 源码简单分析

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 是必须是一个接口,也就是服务,值必须是一个接口的全路径FQNservice 是具体的实现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, 它仅仅是在测试环境中使用。

posted on 2017-08-15 13:10 CanntBelieve 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/FlyAway2013/p/7364576.html

Hessian 源码简单分析相关推荐

  1. poco源码简单分析

    自动化工具poco源码简单分析 Airtest简介 Airtest是网易游戏开源的一款UI自动化测试项目,目前处于公开测试阶段,该项目分为AirtestIDE.Airtest.Poco.Testlab ...

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

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

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

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

  4. JSP 编译和运行过程与JSP源码简单分析

    JSP 编译和运行过程与JSP转移源码简单分析 Web容器处理JSP文件请求的执行过程主要包括以下4个部分: 1. 客户端发出Request请求 2. JSP Container 将JSP转译成Ser ...

  5. 线程的3种实现方式并深入源码简单分析实现原理

    前言 本文介绍下线程的3种实现方式并深入源码简单的阐述下原理 三种实现方式 Thread Runnable Callable&Future 深入源码简单刨析 Thread Thread类实现了 ...

  6. reentrantlock失效了?_ReentrantLock 源码简单分析

    JAVA中锁的实现最常见的方式有两种,一种是 synchronized关键字,一种是Lock.实际的开发过程中,要对这两种方式进行取舍. synchronized是基于JVM层面实现的, Lock却是 ...

  7. ChaLearn Gesture Challenge_3:Approximated gradients源码简单分析

    前言 上一篇博文ChaLearn Gesture Challenge_2:examples体验 中简单介绍了CGC官网提供的丰富的sample,本节来简单分下其中的一个sample源码,该sample ...

  8. Linux·内核源码简单分析

    目录 系统总体流程: 各个目录的阅读总结: (一) boot (二)内核初始化init (三)kernel: (四)mm内存管理 (五)文件系统模块fs: 系统总体流程: 系统从boot开始动作,把内 ...

  9. Enemy源码简单分析

    这是这个项目的网页链接,以下是关于enemy源代码的粗略分析. https://github.com/freakanonymous/enemy 弗兰克,是一个全职的恶意代码工程师,会不定期更新enem ...

最新文章

  1. php设置accept,PHP或htaccess通过Accept-Language重写URL?
  2. php开发流程 restful,PhpBoot 入门(一) 快速开发 RESTful 接口
  3. Python笔记:0
  4. MySQL5.7重置root密码
  5. Java基础6:代码块与代码加载顺序
  6. 多媒体计算机软件系统课件,《多媒体计算机系统》PPT课件.ppt
  7. spring cloud alibaba全家桶之nacos
  8. linux桌面图标恢复,修复桌面白图标的教程
  9. 伦敦交通局设置较低的速度限制
  10. python列表索引超出范围 等于啥_Python列表错误,列表索引超出范围
  11. 关于短信群发的简单实现
  12. Windows个性化之稀奇古怪三两式(转)
  13. OpenWrt安装腾讯云DDNS插件
  14. 用C语言实现一个简单的计算器代码
  15. w7桌面计算机图标打不开了,桌面图标打不开,教您桌面图标打不开怎么办
  16. Could not initialize class JDBC.JDBCUtils 已解决
  17. 苹果设计团队正在经历重大变革:三名核心元老离职
  18. 历史上的今天:网景浏览器诞生;ENIAC 首席设计师出生;全球首例全机器人手术...
  19. 【GitHub】在Github主页显示你的个人简历
  20. esxi 自动给虚拟机打快照(定时快照任务)

热门文章

  1. 跨域/非跨域接口专题
  2. 使用RichTextBox控件实现系统剪切板功能
  3. 一些常用正则表达式片段的分析
  4. Git 初始化版本库
  5. android 开发
  6. Ajax.BeginForm无法调用 ajaxOptions的js函数
  7. 最伟大最不可思议最令人感动的父亲
  8. 滑坡泥石流的防御措施_滑坡泥石流防御
  9. gbk编码在线转换工具_TOOLFK工具-在线汉字/字母/人民币/简繁体转换工具
  10. 同一进程中的线程究竟共享哪些资源