GWT RPC原理浅析(二)

前一篇介绍了RPC大体的流程,核心方法是RemoteServiceServlet类中的processPost方法

public final void processPost(HttpServletRequest request,
      HttpServletResponse response) throws IOException, ServletException,
      SerializationException {
    // Read the request fully.
    //
    String requestPayload = readContent(request);

    // Let subclasses see the serialized request.
    //
    onBeforeRequestDeserialized(requestPayload);

    // Invoke the core dispatching logic, which returns the serialized
    // result.
    //
    String responsePayload = processCall(requestPayload);

    // Let subclasses see the serialized response.
    //
    onAfterResponseSerialized(responsePayload);

    // Write the response.
    //
    writeResponse(request, response, responsePayload);
  }

这是一个final方法,无法覆写。不过GWT为我们提供了前置后置方法,供我们覆写:

onBeforeRequestDeserialized请求反序列化之前的处理

onAfterResponseSerialized响应序列化之后的处理

下面我们深入这三个步骤

1.readContent(request)

这里调用了内部的一个方法

protected String readContent(HttpServletRequest request)
      throws ServletException, IOException {
    return RPCServletUtils.readContentAsGwtRpc(request);
  }

解析的工作由RPCServletUtils来处理,进入此方法

public static String readContentAsGwtRpc(HttpServletRequest request)
      throws IOException, ServletException {
      return readContent(request, GWT_RPC_CONTENT_TYPE, CHARSET_UTF8);
  }

调用了同名方法,添加了2个参数,分别是请求格式(text/x-gwt-rpc)编码(utf-8)

进入

public static String readContent(HttpServletRequest request,
      String expectedContentType, String expectedCharSet)
      throws IOException, ServletException {
    if (expectedContentType != null) {
      checkContentTypeIgnoreCase(request, expectedContentType);
    }
    if (expectedCharSet != null) {
      checkCharacterEncodingIgnoreCase(request, expectedCharSet);
    }

    /*
     * Need to support 'Transfer-Encoding: chunked', so do not rely on
     * presence of a 'Content-Length' request header.
     */
    InputStream in = request.getInputStream();
    byte[] buffer = new byte[BUFFER_SIZE];
    ByteArrayOutputStream out = new  ByteArrayOutputStream(BUFFER_SIZE);
    try {
      while (true) {
        int byteCount = in.read(buffer);
        if (byteCount == -1) {
          break;
        }
        out.write(buffer, 0, byteCount);
      }
      String contentCharSet = expectedCharSet != null
          ? expectedCharSet : CHARSET_UTF8;
      return out.toString(contentCharSet);
    } finally {
      if (in != null) {
        in.close();
      }
    }
  }

此处拿到request的输入流,并将内容转换为String类型返回。

至此readContent结束。

2.processCall(Stringpayload)

处理这个请求的RPC调用

public String processCall(String payload) throws SerializationException {
    // First, check for possible XSRF situation
    checkPermutationStrongName();

    try {
      RPCRequest rpcRequest = RPC.decodeRequest(payload, delegate.getClass(), this);
      onAfterRequestDeserialized(rpcRequest);
      return RPC.invokeAndEncodeResponse(delegate, rpcRequest.getMethod(),
          rpcRequest.getParameters(), rpcRequest.getSerializationPolicy(),
          rpcRequest.getFlags());
    } catch (IncompatibleRemoteServiceException ex) {
      log(
          "An IncompatibleRemoteServiceException was thrown while processing this call.",
          ex);
      return RPC.encodeResponseForFailure(null, ex);
    }
  }

RPC.decodeRequest()方法对提交上来的文本解析,生成一个RPCRequest对象

进入此方法

public static RPCRequest decodeRequest(String encodedRequest, Class<?> type,
      SerializationPolicyProvider serializationPolicyProvider) {
   
	//….. 省略部分代码
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    try {
      ServerSerializationStreamReader streamReader = new ServerSerializationStreamReader(
          classLoader, serializationPolicyProvider);
      streamReader.prepareToRead(encodedRequest);

     //省略大量代码
//此处通过序列化读入流,解析文本,得出请求中需要调用的类名,方法名,已经方法签名数据 根据这些参数创建RPCRequest对象返回

        return new RPCRequest(method, parameterValues, serializationPolicy,
            streamReader.getFlags());

      } catch (NoSuchMethodException e) {
        throw new IncompatibleRemoteServiceException(
            formatMethodNotFoundErrorMessage(serviceIntf, serviceMethodName,
                parameterTypes));
      }
    } catch (SerializationException ex) {
      throw new IncompatibleRemoteServiceException(ex.getMessage(), ex);
    }
  }

获得了RPCRequest对象后,通过RPC.invokeAndEncodeResponse()调用业务对象完成RPC

public static String invokeAndEncodeResponse(Object target,
      Method serviceMethod, Object[] args,
      SerializationPolicy serializationPolicy, int flags)
      throws SerializationException {
    if (serviceMethod == null) {
      throw new NullPointerException("serviceMethod");
    }

    if (serializationPolicy == null) {
      throw new NullPointerException("serializationPolicy");
    }

    String responsePayload;
try {
//利用反射,完成对业务对象的调用
      Object result = serviceMethod.invoke(target, args);
//把业务对象调用的返回结果序列化编码 并返回
      responsePayload = encodeResponseForSuccess(serviceMethod, result,
          serializationPolicy, flags);
    } catch (IllegalAccessException e) {
      SecurityException securityException = new SecurityException(
          formatIllegalAccessErrorMessage(target, serviceMethod));
      securityException.initCause(e);
      throw securityException;
    } catch (IllegalArgumentException e) {
      SecurityException securityException = new SecurityException(
          formatIllegalArgumentErrorMessage(target, serviceMethod, args));
      securityException.initCause(e);
      throw securityException;
    } catch (InvocationTargetException e) {
      // Try to encode the caught exception
      //
      Throwable cause = e.getCause();

      responsePayload = encodeResponseForFailure(serviceMethod, cause,
          serializationPolicy, flags);
    }

    return responsePayload;
  }

拿到需要返回的序列化结果后,将其写入response

3.writeResponse

private void writeResponse(HttpServletRequest request,
      HttpServletResponse response, String responsePayload) throws IOException {
    boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
        && shouldCompressResponse(request, response, responsePayload);

    RPCServletUtils.writeResponse(getServletContext(), response,
        responsePayload, gzipEncode);
  }

在写入response时,还判断浏览器是否支持gzip压缩,如果支持,则调用RPCServletUtils.writeResponse方法时,传参gzipEncod=true

当然内容还会判断,如果响应内容长度小于255,还是不会压缩的。

至此RPC整个请求响应完成

相关推荐