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

  • 相关阅读:
    动态修改类注解(赋值)
    Javassist字节码增强示例
    修改原有的方法名称(字节码增强)
    mock测试之powermock
    SpringMVC防止表单重复提交
    Node.js中的异步I/O是如何进行的?
    移动端三个视口
    javascript 正则(将数字转化为三位分隔的样式)
    Struts2中数据封装方式
    Struts2配置文件
  • 原文地址:https://www.cnblogs.com/davidwang456/p/5496933.html
Copyright © 2011-2022 走看看