spring remoting源码分析--Hessian分析

1. Caucho

1.1 概况

spring-remoting代码的情况如下:

本节近分析caucho模块。

1.2 分类

其中以hession为例,Hessian远程服务调用过程:

spring remoting源码分析--Hessian分析_第1张图片

 

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

 

1.2.2 服务器端

HessianExporter及其实现类HessianServiceExporter,SimpleHessianServiceExporter.

 hessian服务端示例

package hessian.test;

import com.caucho.hessian.server.HessianServlet; public class BasicService extends HessianServlet implements Basic { public String hello() { return "Hello, world"; } }

 我们来看一下:

HessianServiceExporter

/**
 * Servlet-API-based HTTP request handler that exports the specified service bean
 * as Hessian service endpoint, accessible via a Hessian proxy.
 *
 * <p><b>Note:</b> Spring also provides an alternative version of this exporter,
 * for Sun's JRE 1.6 HTTP server: {@link SimpleHessianServiceExporter}.
 *
 * <p>Hessian is a slim, binary RPC protocol.
 * For information on Hessian, see the
 * <a href="http://www.caucho.com/hessian">Hessian website</a>.
 * <b>Note: As of Spring 4.0, this exporter requires Hessian 4.0 or above.</b>
 *
 * <p>Hessian services exported with this class can be accessed by
 * any Hessian client, as there isn't any special handling involved.
 *
 * @author Juergen Hoeller
 * @since 13.05.2003
 * @see HessianClientInterceptor
 * @see HessianProxyFactoryBean
 * @see org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
 * @see org.springframework.remoting.rmi.RmiServiceExporter
 */

处理客户端请求的方法:

/**
     * Processes the incoming Hessian request and creates a Hessian response.
     */
    @Override
    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"); } response.setContentType(CONTENT_TYPE_HESSIAN); try { invoke(request.getInputStream(), response.getOutputStream()); } catch (Throwable ex) { throw new NestedServletException("Hessian skeleton invocation failed", ex); } }

invoke调用

/**
     * Actually invoke the skeleton with the given streams.
     * @param skeleton the skeleton to invoke
     * @param inputStream the request stream
     * @param outputStream the response stream
     * @throws Throwable if invocation failed
     */
    protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)
            throws Throwable { ClassLoader originalClassLoader = overrideThreadContextClassLoader(); try { InputStream isToUse = inputStream; OutputStream osToUse = outputStream; if (this.debugLogger != null && this.debugLogger.isDebugEnabled()) { PrintWriter debugWriter = new PrintWriter(new CommonsLogWriter(this.debugLogger)); @SuppressWarnings("resource") HessianDebugInputStream dis = new HessianDebugInputStream(inputStream, debugWriter); @SuppressWarnings("resource") HessianDebugOutputStream dos = new HessianDebugOutputStream(outputStream, debugWriter); dis.startTop2(); dos.startTop2(); isToUse = dis; osToUse = dos; } if (!isToUse.markSupported()) { isToUse = new BufferedInputStream(isToUse); isToUse.mark(1); } int code = isToUse.read(); int major; int minor; AbstractHessianInput in; AbstractHessianOutput out; if (code == 'H') { // Hessian 2.0 stream major = isToUse.read(); minor = isToUse.read(); if (major != 0x02) { throw new IOException("Version " + major + "." + minor + " is not understood"); } in = new Hessian2Input(isToUse); out = new Hessian2Output(osToUse); in.readCall(); } else if (code == 'C') { // Hessian 2.0 call... for some reason not handled in HessianServlet!  isToUse.reset(); in = new Hessian2Input(isToUse); out = new Hessian2Output(osToUse); in.readCall(); } else if (code == 'c') { // Hessian 1.0 call major = isToUse.read(); minor = isToUse.read(); in = new HessianInput(isToUse); if (major >= 2) { out = new Hessian2Output(osToUse); } else { out = new HessianOutput(osToUse); } } else { throw new IOException("Expected 'H'/'C' (Hessian 2.0) or 'c' (Hessian 1.0) in hessian input at " + code); } if (this.serializerFactory != null) { in.setSerializerFactory(this.serializerFactory); out.setSerializerFactory(this.serializerFactory); } if (this.remoteResolver != null) { in.setRemoteResolver(this.remoteResolver); } try { skeleton.invoke(in, out); } finally { try { in.close(); isToUse.close(); } catch (IOException ex) { // ignore  } try { out.close(); osToUse.close(); } catch (IOException ex) { // ignore  } } } finally { resetThreadContextClassLoader(originalClassLoader); } }

调用skeleton的invoke方法

/**
   * Invoke the object with the request from the input stream.
   *
   * @param in the Hessian input stream
   * @param out the Hessian output stream
   */
  public void invoke(Object service,
                     AbstractHessianInput in,
                     AbstractHessianOutput out)
    throws Exception { ServiceContext context = ServiceContext.getContext(); // backward compatibility for some frameworks that don't read // the call type first  in.skipOptionalCall(); // Hessian 1.0 backward compatibility  String header; while ((header = in.readHeader()) != null) { Object value = in.readObject(); context.addHeader(header, value); } String methodName = in.readMethod(); int argLength = in.readMethodArgLength(); Method method; method = getMethod(methodName + "__" + argLength); if (method == null) method = getMethod(methodName); if (method != null) { } else if ("_hessian_getAttribute".equals(methodName)) { String attrName = in.readString(); in.completeCall(); String value = null; if ("java.api.class".equals(attrName)) value = getAPIClassName(); else if ("java.home.class".equals(attrName)) value = getHomeClassName(); else if ("java.object.class".equals(attrName)) value = getObjectClassName(); out.writeReply(value); out.close(); return; } else if (method == null) { out.writeFault("NoSuchMethodException", escapeMessage("The service has no method named: " + in.getMethod()), null); out.close(); return; } Class<?> []args = method.getParameterTypes(); if (argLength != args.length && argLength >= 0) { out.writeFault("NoSuchMethod", escapeMessage("method " + method + " argument length mismatch, received length=" + argLength), null); out.close(); return; } Object []values = new Object[args.length]; for (int i = 0; i < args.length; i++) { // XXX: needs Marshal object values[i] = in.readObject(args[i]); } Object result = null; try { result = method.invoke(service, values); } catch (Exception e) { Throwable e1 = e; if (e1 instanceof InvocationTargetException) e1 = ((InvocationTargetException) e).getTargetException(); log.log(Level.FINE, this + " " + e1.toString(), e1); out.writeFault("ServiceException", escapeMessage(e1.getMessage()), e1); out.close(); return; } // The complete call needs to be after the invoke to handle a // trailing InputStream  in.completeCall(); out.writeReply(result); out.close(); }

反射触发类的方法。

 

BurlapExporter及其实现类BurlapServiceExporter,SimpleBurlapServiceExporter,因已经depressed,故略。

1.3 小结

  Spring封装了hessian客户端和服务端的通用代码,把实现者和调用者作为bean放到spring容器中管理,简化了开发。分析源码的过程中,发现在客户端使用了动态代理,在服务端使用反射,让我们加深了对java基础知识的理解。

 

你可能感兴趣的:(spring remoting源码分析--Hessian分析)