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整个请求响应完成