在上一篇博客 Hessian源码分析--HessianProxyFactory 中我们了解到,客户端获得的对象其实是HessianProxy生成的目标对象,当调用目标对象的方法时,会调用HessianProxy的invoke方法,如下,当调用HelloService的helloWorld函数时,会调用HessianProxy的invoke函数(对代理机制不懂的同学可以去学习一下)。

HessianProxyFactory factory = new HessianProxyFactory();
HelloService helloService = (HelloService) factory.create(HelloService.class, url);
System.out.println(helloService.helloWorld("world"));

接下来我们一步一步分析invoke函数,就会对Hessian的机制有一个比较清楚的了解:

invoke方法参数如下

 /**
   * Handles the object invocation.
   *
   * @param proxy 就是HessianProxy
   * @param method  调用的方法
   * @param args 方法需要的参数
   */
  public Object invoke(Object proxy, Method method, Object []args)
    throws Throwable
    String mangleName;

	//方法对象对应的名称缓存起来
    synchronized (_mangleMap) {
      mangleName = _mangleMap.get(method);
    }

    if (mangleName == null) {
	  //获得方法名称
      String methodName = method.getName();
	  //方法参数对象类型
      Class<?> []params = method.getParameterTypes();

      // equals and hashCode are special cased
	  //如果调用的方法是equals或者hashCode就特殊处理,不用远程调用
      if (methodName.equals("equals")
          && params.length == 1 && params[0].equals(Object.class)) {
        Object value = args[0];
        if (value == null || ! Proxy.isProxyClass(value.getClass()))
          return Boolean.FALSE;
        Object proxyHandler = Proxy.getInvocationHandler(value);
        if (! (proxyHandler instanceof HessianProxy))
          return Boolean.FALSE;
        HessianProxy handler = (HessianProxy) proxyHandler;
        return new Boolean(_url.equals(handler.getURL()));
      }
      else if (methodName.equals("hashCode") && params.length == 0)
        return new Integer(_url.hashCode());
      else if (methodName.equals("getHessianType"))
        return proxy.getClass().getInterfaces()[0].getName();
      else if (methodName.equals("getHessianURL"))
        return _url.toString();
      else if (methodName.equals("toString") && params.length == 0)
        return "HessianProxy[" + _url + "]";

      if (! _factory.isOverloadEnabled())
        mangleName = method.getName();
      else
        mangleName = mangleName(method);

	   //保存函数对象对应的函数名称
      synchronized (_mangleMap) {
        _mangleMap.put(method, mangleName);
      }
    }

这部分操作是Hessian的关键,sendRequest就是向服务端发送请求,这期间还包括对参数值的序列化,发送请求之后,服务端会根据参数来调用服务端函数并将调用结果序列化之后返回,这样就可以通过conn.getInputStream来获得返回结果,

    InputStream is = null;
    HessianConnection conn = null;

    try {
      if (log.isLoggable(Level.FINER))
        log.finer("Hessian[" + _url + "] calling " + mangleName);

	  //向server发送请求,包括函数名及函数的参数
      conn = sendRequest(mangleName, args);
	  //获取请求调用之后的返回值
      is = getInputStream(conn);

      if (log.isLoggable(Level.FINEST)) {
        PrintWriter dbg = new PrintWriter(new LogWriter(log));
        HessianDebugInputStream dIs
          = new HessianDebugInputStream(is, dbg);

        dIs.startTop2();

        is = dIs;
      }

上面我们大体分析了一下sendRequest函数,接下来我们详细介绍一下这个函数的具体操作:

首先会根据URL来通过获得conn连接对象,接下来通过addRequestHeaders来设置一些默认的头部信息,

通过conn.getOutputStream()来获取OutputStream对象,根据OutputStream对象接下来获得Hessian提供的序列化对象out,

调用out.call(methodName, args)来序列化需要远程调用的函数名称和参数值,out.flush();将传递的数据刷新到outputStream中,

最后通过调用 conn.sendRequest();来发送请求,这个时候服务暴露端会得到这个请求(具体请求执行我们接下来分析)

并返回conn,此时调用结果值应该在conn的InputStream中了。

protected HessianConnection sendRequest(String methodName, Object []args)
    throws IOException
  {
    HessianConnection conn = null;
    //根据URL获取链接对象
    conn = _factory.getConnectionFactory().open(_url);
    boolean isValid = false;

    try {
	  //在请求中设置一些头的默认值
      addRequestHeaders(conn);

      OutputStream os = null;

      try {
		//获取OutputStream
        os = conn.getOutputStream();
      } catch (Exception e) {
        throw new HessianRuntimeException(e);
      }

      if (log.isLoggable(Level.FINEST)) {
        PrintWriter dbg = new PrintWriter(new LogWriter(log));
        HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg);
        dOs.startTop2();
        os = dOs;
      }

      AbstractHessianOutput out = _factory.getHessianOutput(os);
	  //对函数名和参数值进行二进制序列化
      out.call(methodName, args);
      out.flush();
      //向暴露的服务端发送请求
      conn.sendRequest();

      isValid = true;

      return conn;
    } finally {
      if (! isValid && conn != null)
        conn.destroy();
    }
  }

当服务暴露端返回值之后,接下来就是返回值的处理了,因为服务端给我们的也是二进制数据,所以我们需要感觉函数参数的类型来反序列化得到结果,上面我们已经分析到

is = getInputStream(conn),这样返回值就在is中,首先第一个读到的是一个code,这样应该是一个标记值,接下来读到的就是主版本号和副版本号了,

in = _factory.getHessian2Input(is)是用来获得反序列化对象

Object value = in.readReply(method.getReturnType()) 这样就可以根据方法的返回类型来反序列化结果值了,这样整个的Hessian的客户端的实现机制就是这样了。

      AbstractHessianInput in;

      int code = is.read();

      if (code == 'H') {
        int major = is.read();
        int minor = is.read();

        in = _factory.getHessian2Input(is);

        Object value = in.readReply(method.getReturnType());

        return value;
      }
      else if (code == 'r') {
        int major = is.read();
        int minor = is.read();

        in = _factory.getHessianInput(is);

        in.startReplyBody();

        Object value = in.readObject(method.getReturnType());

        if (value instanceof InputStream) {
          value = new ResultInputStream(conn, is, in, (InputStream) value);
          is = null;
          conn = null;
        }
        else
          in.completeReply();

        return value;

接下来我们会对序列化和反序列化机制,已经连接请求机制也会简单分析。

HessianProxy的完整源码解析:

public class HessianProxy implements InvocationHandler, Serializable {
  private static final Logger log
    = Logger.getLogger(HessianProxy.class.getName());

  protected HessianProxyFactory _factory;

  private WeakHashMap<Method,String> _mangleMap
    = new WeakHashMap<Method,String>();

  private Class<?> _type;
  private URL _url;

  protected HessianProxy(URL url, HessianProxyFactory factory)
  {
    this(url, factory, null);
  }

  protected HessianProxy(URL url,
                         HessianProxyFactory factory,
                         Class<?> type)
  {
    _factory = factory;
    _url = url;
    _type = type;
  }

  public URL getURL()
  {
    return _url;
  }

  /**
   * Handles the object invocation.
   *
   * @param proxy 就是HessianProxy
   * @param method  调用的方法
   * @param args 方法需要的参数
   */
  public Object invoke(Object proxy, Method method, Object []args)
    throws Throwable
  {
    String mangleName;

	//方法对象对应的名称缓存起来
    synchronized (_mangleMap) {
      mangleName = _mangleMap.get(method);
    }

    if (mangleName == null) {
	  //获得方法名称
      String methodName = method.getName();
	  //方法参数对象类型
      Class<?> []params = method.getParameterTypes();

      // equals and hashCode are special cased
	  //如果调用的方法是equals或者hashCode就特殊处理,不用远程调用
      if (methodName.equals("equals")
          && params.length == 1 && params[0].equals(Object.class)) {
        Object value = args[0];
        if (value == null || ! Proxy.isProxyClass(value.getClass()))
          return Boolean.FALSE;
        Object proxyHandler = Proxy.getInvocationHandler(value);
        if (! (proxyHandler instanceof HessianProxy))
          return Boolean.FALSE;
        HessianProxy handler = (HessianProxy) proxyHandler;
        return new Boolean(_url.equals(handler.getURL()));
      }
      else if (methodName.equals("hashCode") && params.length == 0)
        return new Integer(_url.hashCode());
      else if (methodName.equals("getHessianType"))
        return proxy.getClass().getInterfaces()[0].getName();
      else if (methodName.equals("getHessianURL"))
        return _url.toString();
      else if (methodName.equals("toString") && params.length == 0)
        return "HessianProxy[" + _url + "]";

      if (! _factory.isOverloadEnabled())
        mangleName = method.getName();
      else
        mangleName = mangleName(method);

	   //保存函数对象对应的函数名称
      synchronized (_mangleMap) {
        _mangleMap.put(method, mangleName);
      }
    }

    InputStream is = null;
    HessianConnection conn = null;

    try {
      if (log.isLoggable(Level.FINER))
        log.finer("Hessian[" + _url + "] calling " + mangleName);

	  //向server发送请求,包括函数名及函数的参数
      conn = sendRequest(mangleName, args);
	  //获取请求调用之后的返回值
      is = getInputStream(conn);

      if (log.isLoggable(Level.FINEST)) {
        PrintWriter dbg = new PrintWriter(new LogWriter(log));
        HessianDebugInputStream dIs
          = new HessianDebugInputStream(is, dbg);

        dIs.startTop2();

        is = dIs;
      }

      AbstractHessianInput in;
	  //获取返回码
      int code = is.read();

      if (code == 'H') {
        int major = is.read();
        int minor = is.read();
		//获得对象Hessian2Input
        in = _factory.getHessian2Input(is);
		//根据方法返回值类型,反序列化获得返回值
        Object value = in.readReply(method.getReturnType());

        return value;
      }
      else if (code == 'r') {
        int major = is.read();
        int minor = is.read();

        in = _factory.getHessianInput(is);

        in.startReplyBody();

        Object value = in.readObject(method.getReturnType());

        if (value instanceof InputStream) {
          value = new ResultInputStream(conn, is, in, (InputStream) value);
          is = null;
          conn = null;
        }
        else
          in.completeReply();
		//得到远程调用结果
        return value;
      }
      else
        throw new HessianProtocolException("'" + (char) code + "' is an unknown code");
    } catch (HessianProtocolException e) {
      throw new HessianRuntimeException(e);
    } finally {
      try {
        if (is != null)
          is.close();
      } catch (Exception e) {
        log.log(Level.FINE, e.toString(), e);
      }

      try {
        if (conn != null)
          conn.destroy();
      } catch (Exception e) {
        log.log(Level.FINE, e.toString(), e);
      }
    }
  }

  //获取HessianConnection中的返回值
  protected InputStream getInputStream(HessianConnection conn)
    throws IOException
  {
    InputStream is = conn.getInputStream();

    if ("deflate".equals(conn.getContentEncoding())) {
      is = new InflaterInputStream(is, new Inflater(true));
    }

    return is;
  }

  protected String mangleName(Method method)
  {
    Class<?> []param = method.getParameterTypes();

    if (param == null || param.length == 0)
      return method.getName();
    else
      return AbstractSkeleton.mangleName(method, false);
  }

  /**
   * Sends the HTTP request to the Hessian connection.
   */
  protected HessianConnection sendRequest(String methodName, Object []args)
    throws IOException
  {
    HessianConnection conn = null;
    //根据URL获取链接对象
    conn = _factory.getConnectionFactory().open(_url);
    boolean isValid = false;

    try {
	  //在请求中设置一些头的默认值
      addRequestHeaders(conn);

      OutputStream os = null;

      try {
		//获取OutputStream
        os = conn.getOutputStream();
      } catch (Exception e) {
        throw new HessianRuntimeException(e);
      }

      if (log.isLoggable(Level.FINEST)) {
        PrintWriter dbg = new PrintWriter(new LogWriter(log));
        HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg);
        dOs.startTop2();
        os = dOs;
      }

      AbstractHessianOutput out = _factory.getHessianOutput(os);
	  //对函数名和参数值进行二进制序列化
      out.call(methodName, args);
      out.flush();
      //向暴露的服务端发送请求
      conn.sendRequest();

      isValid = true;

      return conn;
    } finally {
      if (! isValid && conn != null)
        conn.destroy();
    }
  }

  //在请求中设置一些头的默认值
  protected void addRequestHeaders(HessianConnection conn)
  {
    conn.addHeader("Content-Type", "x-application/hessian");
    conn.addHeader("Accept-Encoding", "deflate");

    String basicAuth = _factory.getBasicAuth();

    if (basicAuth != null)
      conn.addHeader("Authorization", basicAuth);
  }

  /**
   * Method that allows subclasses to parse response headers such as cookies.
   * Default implementation is empty.
   * @param conn
   */
  protected void parseResponseHeaders(URLConnection conn)
  {
  }

  public Object writeReplace()
  {
    return new HessianRemote(_type.getName(), _url.toString());
  }

  static class ResultInputStream extends InputStream {
    private HessianConnection _conn;
    private InputStream _connIs;
    private AbstractHessianInput _in;
    private InputStream _hessianIs;

    ResultInputStream(HessianConnection conn,
                      InputStream is,
                      AbstractHessianInput in,
                      InputStream hessianIs)
    {
      _conn = conn;
      _connIs = is;
      _in = in;
      _hessianIs = hessianIs;
    }

    public int read()
      throws IOException
    {
      if (_hessianIs != null) {
        int value = _hessianIs.read();

        if (value < 0)
          close();

        return value;
      }
      else
        return -1;
    }

    public int read(byte []buffer, int offset, int length)
      throws IOException
    {
      if (_hessianIs != null) {
        int value = _hessianIs.read(buffer, offset, length);

        if (value < 0)
          close();

        return value;
      }
      else
        return -1;
    }

    public void close()
      throws IOException
    {
      HessianConnection conn = _conn;
      _conn = null;

      InputStream connIs = _connIs;
      _connIs = null;

      AbstractHessianInput in = _in;
      _in = null;

      InputStream hessianIs = _hessianIs;
      _hessianIs = null;

      try {
        if (hessianIs != null)
          hessianIs.close();
      } catch (Exception e) {
        log.log(Level.FINE, e.toString(), e);
      }

      try {
        if (in != null) {
          in.completeReply();
          in.close();
        }
      } catch (Exception e) {
        log.log(Level.FINE, e.toString(), e);
      }

      try {
        if (connIs != null) {
          connIs.close();
        }
      } catch (Exception e) {
        log.log(Level.FINE, e.toString(), e);
      }

      try {
        if (conn != null) {
          conn.close();
        }
      } catch (Exception e) {
        log.log(Level.FINE, e.toString(), e);
      }
    }
  }
  //日志相关
  static class LogWriter extends Writer {
    private Logger _log;
    private Level _level = Level.FINEST;
    private StringBuilder _sb = new StringBuilder();

    LogWriter(Logger log)
    {
      _log = log;
    }

    public void write(char ch)
    {
      if (ch == '\n' && _sb.length() > 0) {
        _log.fine(_sb.toString());
        _sb.setLength(0);
      }
      else
        _sb.append((char) ch);
    }

    public void write(char []buffer, int offset, int length)
    {
      for (int i = 0; i < length; i++) {
        char ch = buffer[offset + i];

        if (ch == '\n' && _sb.length() > 0) {
          _log.log(_level, _sb.toString());
          _sb.setLength(0);
        }
        else
          _sb.append((char) ch);
      }
    }

    public void flush()
    {
    }

    public void close()
    {
      if (_sb.length() > 0)
        _log.log(_level, _sb.toString());
    }
  }
}

Hessian源码分析--HessianProxy的更多相关文章

  1. Hessian源码分析--总体架构

    Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单.快捷.采用的是二进制RPC协议,因为采用的是二进制协 ...

  2. Hessian源码分析--HessianSkeleton

    HessianSkeleton是Hessian的服务端的核心,简单总结来说:HessianSkeleton根据客户端请求的链接,获取到需要执行的接口及实现类,对客户端发送过来的二进制数据进行反序列化, ...

  3. (转)hessian源码分析(一)------架构

    在计费中心的对外交互这块采用了hessian,有必要对hessian的运行机理和源码做一定的解析. 大致翻了翻源码后,发现hessian的主要结构分客户端与服务端,中间基于http传输.客户端主要做的 ...

  4. Hessian源码分析--HessianProxyFactory

    HessianProxyFactory是HessianProxy的工厂类,其通过HessianProxy来生成代理类. 如下面代码: HessianProxyFactory factory = new ...

  5. Hessian源码分析--HessianServlet

    Hessian可以通过Servlet来对外暴露服务,HessianServlet继承于HttpServlet,但这仅仅是一个外壳,使用web服务器来提供对外的Http请求,在web.xml中我们会进行 ...

  6. SURF算法与源码分析、下

    上一篇文章 SURF算法与源码分析.上 中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述.这一步至关重要,这是SU ...

  7. Dubbo 源码分析 - 服务引用

    1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...

  8. 【OpenCV】SIFT原理与源码分析:关键点搜索与定位

    <SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一步<DoG尺度空间构造>,我们得到了 ...

  9. 12.源码分析—如何为SOFARPC写一个序列化?

    SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...

随机推荐

  1. Dynamics 365中使用Web API将查找字段的值设置为空值的方法。

    摘要: 本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复270或者20180424可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyon ...

  2. java常用的几种线程池比较

    1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...

  3. FreeMarker学习教程

    copy自http://demojava.iteye.com/blog/800204 以下内容全部是网上收集: FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主 ...

  4. 罗列Linux发行版的基础目录名称,命令法则和功能

    罗列Linux发行版的基础目录名称命名法则及功用规定 目录描述 /主层次 的根,也是整个文件系统层次结构的根目录 /bin存放在单用户模式可用的必要命令二进制文件,所有用户都可用,如 cat.ls.c ...

  5. 什么样的简历受HR青睐?

    简历是我们在求职过程中的名片,那么如何写出更容易受到HR青睐的简历呢? HR可能一天要看上百份的简历,他们都希望能够尽快筛选出合适的人,然后用更多的时间去跟候选人沟通.所以招聘人员一般看一份简历只会花 ...

  6. spring cloud 入门系列四:使用Hystrix 实现断路器进行服务容错保护

    在微服务中,我们将系统拆分为很多个服务单元,各单元之间通过服务注册和订阅消费的方式进行相互依赖.但是如果有一些服务出现问题了会怎么样? 比如说有三个服务(ABC),A调用B,B调用C.由于网络延迟或C ...

  7. APP自动化框架LazyAndroid使用手册(3)--核心API介绍

    作者:黄书力 概述 在前一篇博文中,简要介绍了一款安卓UI自动化测试框架LazyAndroid (http://blog.csdn.net/kaka1121/article/details/53204 ...

  8. Angular2学习笔记2

    每个angular2应用程序默认使用app目录来创建(可以自己制定,但是eclipse插件生成的会自动使用app) 每个程序应当至少有一个angular模块即根模块.根模块使用@NgModule({} ...

  9. Swift3中如何为Array写一个限定Type的扩展

    我们知道Swift可以扩展已存在的类或结构,这些类或结构可以存在于标准库(或称为核心库)中.如果结构是一个集合类型(比如Array)就更有趣了.我们想尝试写一个限定Type数组的扩展,So我们就拿Ar ...

  10. python将nan, inf转为特定的数字

    最近,处理两个矩阵的点除,得到结果后,再作其他的计算,发现有些内置的函数不work:查看得到的数据,发现有很多nan和inf,导致python的基本函数运行不了,这是因为在除的过程中分母出现0的缘故. ...