zoukankan      html  css  js  c++  java
  • spring remoting源码分析--Hessian分析

    1. Caucho

    1.1 概况

    spring-remoting代码的情况如下:

    本节近分析caucho模块。

    1.2 分类

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

                              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基础知识的理解。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 奇偶判断
    Java实现 蓝桥杯VIP 算法训练 传球游戏
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 Hanoi问题
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Java实现 蓝桥杯VIP 算法训练 蜜蜂飞舞
    Qt: 访问容器(三种方法,加上for循环就四种了)good
  • 原文地址:https://www.cnblogs.com/davidwang456/p/5496933.html
Copyright © 2011-2022 走看看