Spring远程服务编程框架 Spring Remoting——Hessian远程服务调用过程

章承基
2023-12-01

Remoting实际上是一种企业分布式计算的组件。在同一服务器(Java虚拟机)内进行调用的服务(或类)并不需要把自己暴露为一种远程服务; 但是,如果你需要与一个外部程序(在不同的服务器上或在一个不同的组织中)进行通讯的话,那么,必须把它实现为一个远程服务。Spring框架为把业务类 暴露为远程服务提供了一种独特而灵活的方式。

Spring Remoting架构的核心是服务对象,这些对象其实是一些POJO,也称作Spring bean。Spring框架能够把这些服务对象与基础结构细节(例如它们暴露为远程服务的方式)隔离开来;这样以来,开发者就可以专注于实现服务对象的业 务接口而不是牵涉到这些细节中去。

这个Remoting模型提供了对业务服务的远程抽象。它负责编排和反编排方法参数并且还负责处理服务方法中抛出的任何异常—使用未检查的RemoteAccessException异常对它们进行包装。为了实现各种服务,Spring使用了若干设计模式。例如,它使用代理设计模式把你对HTTP POST请求的调用翻译成指向输出服务的URL。

Spring目前提供了对RMI、HttpInvoker、Hessian、Burlap及WebService等Remoting技术的集成。Spring屏蔽了这些实现技术的差异,用户只需开发简单的Java对象(Plain Old Java Objects,POJO)然后按照Spring规定的格式进行配置文件的编写即可。

         Hessian远程服务调用过程

1.2.1 客户端

BurlapProxyFactoryBean,BurlapClientInterceptor;

HessianProxyFactoryBean,HessianClientInterceptor;

HessianProxyFactoryBean继承自HessianClientInterceptor,间接封装了HessianProxyFactory。HessianProxyFactory是hessian的client实现类,

示例:


public interface Basic {
  public String hello();
}

import com.caucho.hessian.client.HessianProxyFactory;

public class BasicClient {
  public static void main(String []args)
    throws Exception
  {
    String url = "http://www.caucho.com/hessian/test/basic";

    HessianProxyFactory factory = new HessianProxyFactory();
    Basic basic = (Basic) factory.create(Basic.class, url);

    System.out.println("Hello: " + basic.hello());
  }
}


 create方法如下:



/**
   * Creates a new proxy with the specified URL.  The returned object
   * is a proxy with the interface specified by api.
   *
   * <pre>
   * String url = "http://localhost:8080/ejb/hello");
   * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
   * </pre>
   *
   * @param api the interface the proxy class needs to implement
   * @param url the URL where the client object is located.
   *
   * @return a proxy to the object with the specified interface.
   */
  public Object create(Class<?> api, URL url, ClassLoader loader)
  {
    if (api == null)
      throw new NullPointerException("api must not be null for HessianProxyFactory.create()");
    InvocationHandler handler = null;

    handler = new HessianProxy(url, this, api);

    return Proxy.newProxyInstance(loader,
                                  new Class[] { api,
                                                HessianRemoteObject.class },
                                  handler);
  }
 

 

其中HessianProxy实现了java的动态代理

 

/**
 * Proxy implementation for Hessian clients.  Applications will generally
 * use HessianProxyFactory to create proxy clients.
 */
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 constructor for subclassing
   */
  protected HessianProxy(URL url, HessianProxyFactory factory)
  {
    this(url, factory, null);
  }

  /**
   * Protected constructor for subclassing
   */
  protected HessianProxy(URL url,
                         HessianProxyFactory factory, 
                         Class<?> type)
  {
    _factory = factory;
    _url = url;
    _type = type;
  }
}

 

 最重要的invoke方法如下:

/**
   * Handles the object invocation.
   *
   * @param proxy the proxy object to invoke
   * @param method the method to call
   * @param args the arguments to the proxy object
   */
  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
      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);
      
      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();

        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);
      }
    }
  }

 

发送http请求

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

    try {
      addRequestHeaders(conn);

      OutputStream os = null;

      try {
        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();
    }
  }
 

 

创建http连接代码

 

 /**
   * Opens a new or recycled connection to the HTTP server.
   */
  public HessianConnection open(URL url)
    throws IOException
  {
    if (log.isLoggable(Level.FINER))
      log.finer(this + " open(" + url + ")");

    URLConnection conn = url.openConnection();

    // HttpURLConnection httpConn = (HttpURLConnection) conn;
    // httpConn.setRequestMethod("POST");
    // conn.setDoInput(true);

    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) {
      }
    }

 

 类似资料: