Hessian源码剖析(三)

上篇博文我们分析了一个远程调用的过程,但是没有分析具体的细节,这篇文章我们来分析一下远程调用请求的过程

protected HessianConnection sendRequest(String methodName, Object []args)
    throws IOException
  {
    HessianConnection conn = null;

    conn = _factory.getConnectionFactory().open(_url);//代码1
    boolean isValid = false;

    try {
      addRequestHeaders(conn);//代码2

      OutputStream os = null;

      try {
        os = conn.getOutputStream();//代码3
      } catch (Exception e) {
        throw new HessianRuntimeException(e);
      }

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

      AbstractHessianOutput out = _factory.getHessianOutput(os);//代码5

      out.call(methodName, args);//代码6
      out.flush();//代码7

      conn.sendRequest();//代码8

      isValid = true;

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

1、_factory是在创建HessianProxy代理对象时,通过构造器组合进来的HessianProxyFactory对象,HessianProxyFactory组合了两个工厂,一个是HessianConnectionFactory工厂,它负责创建到Server端的HTTP连接,代码1就是通过该工厂打开了到server端的连接,另一个是SerializerFactory工厂,该工厂负责将client端的信息进行序列化然后传输到server端,具体代码如下

public HessianConnection open(URL url)
    throws IOException
  {
    if (log.isLoggable(Level.FINER))
      log.finer(this + " open(" + url + ")");

    URLConnection conn = url.openConnection();

    long connectTimeout = _proxyFactory.getConnectTimeout();

    if (connectTimeout >= 0)
      conn.setConnectTimeout((int) connectTimeout);

    conn.setDoOutput(true);

    long readTimeout = _proxyFactory.getReadTimeout();

    if (readTimeout > 0) {
      try {
        conn.setReadTimeout((int) readTimeout);
      } catch (Throwable e) {
      }
    }
    
    return new HessianURLConnection(url, conn);
  }
2、打开了到server端的连接之后就可以通过连接拿到输出流,并向输出流里面写入信息了,但是因为这是一个HTTP请求,所以我们在请求之前要设置对应的请求头,如代码2所示,具体代码如下

  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);
  }
3、通过连接Connection拿到输出流

4、因为默认的日志级别是Info,所以不会走到代码4的判断条件里

5、通过HessianProxyFactory获取经过Hession包装过的输出流AbstractHessianOutput,AbstractHessianOutput只是一个抽象类,真正的包装类是HessianOutput,创建完HessianOutput对象后需要给该对象组合一个SerializerFactory工厂用于client端参数的序列化,代码如下

  public AbstractHessianOutput getHessianOutput(OutputStream os)
  {
    AbstractHessianOutput out;

    if (_isHessian2Request)//默认为false,不会走到这里
      out = new Hessian2Output(os);
    else {
      HessianOutput out1 = new HessianOutput(os);
      out = out1;

      if (_isHessian2Reply)
        out1.setVersion(2);
    }

    out.setSerializerFactory(getSerializerFactory());

    return out;
  }
6、调用HessianOutput对象的call()方法如代码6所示,call()方法的具体实现如下,在call方法中最复杂的当属writeObject()方法了,因为参数的类型是千变万化的,即可能有java的基本类型,又可能有自定义的类型,还可以是list,Map等嵌套的类型,如何将这些信息按一定的规则写出到输出流是Hessian的核心与难点,下篇文章我们来具体分析

  public void call(String method, Object []args)
    throws IOException
  {
    int length = args != null ? args.length : 0;
    
    startCall(method, length);//首先将方法名称写到输出流
    
    for (int i = 0; i < length; i++)
      writeObject(args[i]);//将参数写出到输出流
    
    completeCall();//将结束符写出到输出流
  }
7、代码7将输出缓冲区中的字节流写到网络中

8、将字节流写入到server端后等待server端返回状态码,如果是异常的(非200的状态码)状态码则进行相应的处理

到此为止字节流就已经写到了server端


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值