1. 程式人生 > >SpringMVC原始碼解析(四)之關於json,xml的自動轉換原理

SpringMVC原始碼解析(四)之關於json,xml的自動轉換原理

            關於json,xml的自動轉換原理的核心就在messageConvert,前一篇我們已經分析到通過messageConvert對請求引數進行解析讀取,那就續點分析。

            本節就以json的轉換為例(xml類同只是不同的messageConvert)來進行分析,在我們對請求引數解析時候回顧下readWithMessageConverters方法

body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);

通過converter讀取請求訊息

         那麼問題來了,這些messageConverts哪來的呢?

         我們知道RequestMappingHandlerAdapter是請求處理的介面卡,也就是請求之後處理具體邏輯的執行,關係到哪個類的哪個方法以及轉換器等工作,這個類是我們講的重點,其中它的屬性messageConverters就是我們關注的重點

        RequestMappingHandlerAdapter有個預設建構函式

public RequestMappingHandlerAdapter() {
   StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter
(); stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); this.messageConverters
.add(new SourceHttpMessageConverter<Source>()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); }
很明顯在建立物件例項的時候 就對messageConverters進行了賦值,最後新增的AllEncompassingFormHttpMessageConverter    所有包含的FormHttpMessageConverter,看看究竟有哪些


if條件的值 則是

private static final boolean jaxb2Present =
      ClassUtils.isPresent("javax.xml.bind.Binder", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

private static final boolean jackson2Present =
      ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader()) &&
            ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

private static final boolean jackson2XmlPresent =
      ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

private static final boolean gsonPresent =
      ClassUtils.isPresent("com.google.gson.Gson", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

也就是當com.fasterxml.jackson.databind.ObjectMapper和com.fasterxml.jackson.core.JsonGenerator存在在classpath中才會去載入MappingJackson2HttpMessageConverter。(其他一樣的道理)

現在是不是知道了 為什麼 springMVC預設就支援json轉換器,不過這裡我用fastjson作為分析例子

messageConverter的read方法

public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException {
   return readInternal(clazz, inputMessage);
}
FastJsonHttpMessageConverter的readInternal方法為:
protected Object readInternal(Class<? extends Object> clazz, //
HttpInputMessage inputMessage //
) throws IOException, HttpMessageNotReadableException {

    InputStream in = inputMessage.getBody();
    return JSON.parseObject(in, fastJsonConfig.getCharset(), clazz, fastJsonConfig.getFeatures());
}
可以看到就是把請求訊息體輸入流轉換為json物件,這就是整個訊息轉化為json的過程

下面我們解析分析 是如何將@ResponseBody註解標準的Controller方法返回的java物件轉換為json的

還記得之前RequestMappingHandlerAdapter中invokeAndHandle方法麼,其中也包含了對返回值的處理

this.returnValueHandlers.handleReturnValue(
      returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

handleReturnValule方法的具體實現:

public void handleReturnValue(Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
   if (handler == null) {
      throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
   }
   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
首先通過返回值以及返回型別搜尋對應的returnValueHandler,其次再通過得到的handler對返回訊息進行處理

handleRreturnValue的實現為

public void handleReturnValue(Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
      throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

   mavContainer.setRequestHandled(true);

   // Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, webRequest);
}
沒有似曾相識的感覺 之前我們分析解析請求訊息時候 有個readWithMessageConverters 現在又有個writeWithMessageConverters方法,那我們看看具體實現:


方法中程式碼太多,中間一段略過。。。。。


write方法是程式碼如下:

public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage)
      throws IOException, HttpMessageNotWritableException {

   final HttpHeaders headers = outputMessage.getHeaders();
   addDefaultHeaders(headers, t, contentType);

   if (outputMessage instanceof StreamingHttpOutputMessage) {
      StreamingHttpOutputMessage streamingOutputMessage =
            (StreamingHttpOutputMessage) outputMessage;
      streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
         @Override
public void writeTo(final OutputStream outputStream) throws IOException {
            writeInternal(t, new HttpOutputMessage() {
               @Override
public OutputStream getBody() throws IOException {
                  return outputStream;
               }
               @Override
public HttpHeaders getHeaders() {
                  return headers;
               }
            });
         }
      });
   }
   else {
      writeInternal(t, outputMessage);
      outputMessage.getBody().flush();
   }
}
我們重點關注writeInternal方法的實現,在FastJsonHttpMessageConverter中為
protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException {
    HttpHeaders headers = outputMessage.getHeaders();
    ByteArrayOutputStream outnew = new ByteArrayOutputStream();

    boolean writeAsToString = false;
    if (obj != null) {
        String className = obj.getClass().getName();
        if ("com.fasterxml.jackson.databind.node.ObjectNode".equals(className)) {
            writeAsToString = true;
        }
    }

    if (writeAsToString) {
        String text = obj.toString();
        OutputStream out = outputMessage.getBody();
        out.write(text.getBytes());
        if (fastJsonConfig.isWriteContentLength()) {
            headers.setContentLength(text.length());
        }
    } else {
        int len = JSON.writeJSONString(outnew, //
fastJsonConfig.getCharset(), //
obj, //
fastJsonConfig.getSerializeConfig(), //
fastJsonConfig.getSerializeFilters(), //
fastJsonConfig.getDateFormat(), //
JSON.DEFAULT_GENERATE_FEATURE, //
fastJsonConfig.getSerializerFeatures());
        if (fastJsonConfig.isWriteContentLength()) {
            headers.setContentLength(len);
        }

        OutputStream out = outputMessage.getBody();
        outnew.writeTo(out);
    }


    outnew.close();
}

此方法中將Java物件轉換為json字串

其他訊息轉換器類似,如果讀者感興趣可自己去檢視程式碼研究,這裡就不作分析拉