SpringMVC原理(7)-返回值的处理

138 阅读10分钟

我们在前后端分离模式最常用的返回值就是返回json类型,那SpringMVC是如何把返回值转为json的?这就需要用到返回值处理器了,所以我们一起来看看它的原理吧。

不知道怎么到这一步的,可以看这篇文章:SpringMVC原理(5)-目标方法的执行

 @ResponseBody
 @GetMapping("/addUser")
 public User addUser(User user) {
     return user;
 }
 ​
 @ResponseBody
 @GetMapping("/getUser")
 public String getUser() {
     return "getUser";
 }
 ​
 ...

invokeForRequest()执行完拿到返回值以后,接下来就是使用返回值处理器来处理返回值了。

 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                             Object... providedArgs) throws Exception {
 ​
     // 1、执行目标方法,拿到返回值
     Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
     setResponseStatus(webRequest);
     
     Assert.state(this.returnValueHandlers != null, "No return value handlers");
     try {
         // 2、利用返回值处理器来处理返回值
         this.returnValueHandlers.handleReturnValue(
             returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
     }
     catch (Exception ex) {
         if (logger.isTraceEnabled()) {
             logger.trace(formatErrorForReturnValue(returnValue), ex);
         }
         throw ex;
     }
 }

处理返回值-handleReturnValue()

 public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                               ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
 ​
     // 1、找到一个返回值处理器
     HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
     if (handler == null) {
         throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
     }
     // 2、利用返回值处理器来处理返回值
     handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
 }
  1. selectHandler():遍历所有返回值处理器,找到能够处理这种返回值的

  2. handleReturnValue():利用返回值处理器来处理返回值

1、找到一个返回值处理器-selectHandler()

 private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
     // 是否是异步的
     boolean isAsyncValue = isAsyncReturnValue(value, returnType);
     for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
         if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
             continue;
         }
         // 判断是否支持处理这种返回值类型
         if (handler.supportsReturnType(returnType)) {
             return handler;
         }
     }
     return null;
 }

其实遍历所有的返回值处理器,调用supportsReturnType()来判断是否支持处理这种返回值类型

默认有15个返回值处理器

image.png

2、利用返回值处理器处理返回值-handleReturnValue()

     public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                   ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
         throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
     ​
         mavContainer.setRequestHandled(true);
         ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
         ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
     ​
         // 利用 HttpMessageConverter 来处理返回值
         writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
     }

所以返回值处理器也是利用HttpMessageConverter来写数据的。所以我们把这个类搞清楚也就明白了。

不知道大家还记得吗,在参数解析这一篇的原理中我们分析了@RequestBody注解也是由HttpMessageConverter来处理的

参数解析原理SpringMVC原理(6)-目标方法执行前的参数处理之参数解析器

@ResponseBody注解的处理

json的返回值会被这个类处理RequestResponseBodyMethodProcessor

     protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
                                                   ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
         throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
     ​
         Object body;
         Class<?> valueType;
         Type targetType;
     ​
         // 返回值是否是字符串类型
         if (value instanceof CharSequence) {
             body = value.toString();
             valueType = String.class;
             targetType = String.class;
         }
         else {
             body = value;
             // 获取返回值类型
             valueType = getReturnValueType(body, returnType);
             targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
         }
     ​
         // 返回值是否是资源类型
         if (isResourceType(value, returnType)) {
             outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
             if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
                 outputMessage.getServletResponse().getStatus() == 200) {
                 Resource resource = (Resource) value;
                 try {
                     List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
                     outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
                     body = HttpRange.toResourceRegions(httpRanges, resource);
                     valueType = body.getClass();
                     targetType = RESOURCE_REGION_LIST_TYPE;
                 }
                 catch (IllegalArgumentException ex) {
                     outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
                     outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
                 }
             }
         }
     ​
         // 选定的媒体类型
         MediaType selectedMediaType = null;
         // 获取 Content-Type 的值
         MediaType contentType = outputMessage.getHeaders().getContentType();
         boolean isContentTypePreset = contentType != null && contentType.isConcrete();
         if (isContentTypePreset) {
             if (logger.isDebugEnabled()) {
                 logger.debug("Found 'Content-Type:" + contentType + "' in response");
             }
             selectedMediaType = contentType;
         }
         else {
             HttpServletRequest request = inputMessage.getServletRequest();
             // 利用内容协商管理器获取客户端支持接收的媒体类型
             List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
             // 获取服务端支持处理的媒体类型
             List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
     ​
             if (body != null && producibleTypes.isEmpty()) {
                 throw new HttpMessageNotWritableException(
                     "No converter found for return value of type: " + valueType);
             }
             List<MediaType> mediaTypesToUse = new ArrayList<>();
             
             // 得到可以使用的媒体类型
             for (MediaType requestedType : acceptableTypes) {
                 for (MediaType producibleType : producibleTypes) {
                     if (requestedType.isCompatibleWith(producibleType)) {
                         mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
                     }
                 }
             }
             if (mediaTypesToUse.isEmpty()) {
                 if (body != null) {
                     throw new HttpMediaTypeNotAcceptableException(producibleTypes);
                 }
                 if (logger.isDebugEnabled()) {
                     logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
                 }
                 return;
             }
     ​
             MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
     ​
             // 得到最终选择的媒体类型
             for (MediaType mediaType : mediaTypesToUse) {
                 if (mediaType.isConcrete()) {
                     selectedMediaType = mediaType;
                     break;
                 }
                 else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
                     selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                     break;
                 }
             }
     ​
             if (logger.isDebugEnabled()) {
                 logger.debug("Using '" + selectedMediaType + "', given " +
                              acceptableTypes + " and supported " + producibleTypes);
             }
         }
     ​
         // 利用 HttpMessageConverter 写出去
         if (selectedMediaType != null) {
             selectedMediaType = selectedMediaType.removeQualityValue();
             
             // 遍历所有 HttpMessageConverter
             for (HttpMessageConverter<?> converter : this.messageConverters) {
                 GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                                                                 (GenericHttpMessageConverter<?>) converter : null);
                 // 判断这种HttpMessageConverter是否支持这种媒体类型
                 if (genericConverter != null ?
                     ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                     converter.canWrite(valueType, selectedMediaType)) {
                     body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                                                        (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                                                        inputMessage, outputMessage);
                     if (body != null) {
                         Object theBody = body;
                         LogFormatUtils.traceDebug(logger, traceOn ->
                                                   "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                         addContentDispositionHeader(inputMessage, outputMessage);
                         if (genericConverter != null) {
                             genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                         }
                         else {
                             // 把数据以json方式写出去
                             ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                         }
                     }
                     else {
                         if (logger.isDebugEnabled()) {
                             logger.debug("Nothing to write: null body");
                         }
                     }
                     return;
                 }
             }
         }
     ​
         if (body != null) {
             Set<MediaType> producibleMediaTypes =
                 (Set<MediaType>) inputMessage.getServletRequest()
                 .getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
     ​
             if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
                 throw new HttpMessageNotWritableException(
                     "No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
             }
             throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
         }
     }

全部流程

  1. 获取返回值类型

  2. 判断返回值是否是类型(我们返回值也可以写类型)

  3. 获取选定的媒体类型

    1. 获取客户端可以接收的媒体类型:getAcceptableMediaTypes()
    2. 获取服务端可以处理的媒体类型:getProducibleMediaTypes()
    3. 通过双层for循环拿到兼容的媒体类型,然后最后再通过一个for循环拿到一个最佳匹配
  4. 利用HttpMessageConverter把返回值写出去

这块逻辑比较复杂,我把它分为下面几步来说。

1、获取客户端可以接收的媒体类型-getAcceptableMediaTypes()

就是获取请求头Accept的值

这一步用到了内容协商管理器,后面可以单独写一篇文章来说,下面也有一些介绍。

     // 利用内容协商管理器解析媒体类型
     private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
         throws HttpMediaTypeNotAcceptableException {
     ​
         return this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
     }
     ​
     // 解析媒体类型
     public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
         for (ContentNegotiationStrategy strategy : this.strategies) {
             List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
             if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
                 continue;
             }
             return mediaTypes;
         }
         return MEDIA_TYPE_ALL_LIST;
     }
     ​
     public List<MediaType> resolveMediaTypes(NativeWebRequest request)
         throws HttpMediaTypeNotAcceptableException {
     ​
         // 获取请求头 Accept 的值
         String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
         if (headerValueArray == null) {
             return MEDIA_TYPE_ALL_LIST;
         }
     ​
         List<String> headerValues = Arrays.asList(headerValueArray);
         try {
             List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
             MediaType.sortBySpecificityAndQuality(mediaTypes);
             return !CollectionUtils.isEmpty(mediaTypes) ? mediaTypes : MEDIA_TYPE_ALL_LIST;
         }
     }

内容协商管理器ContentNegotiationManager

image.png

2、获取服务端可以处理的媒体类型-getProducibleMediaTypes()

遍历所有的HttpMessageConverter,看谁支持处理这种返回值,如果支持就拿到它能支持的媒体类型放入集合中

     protected List<MediaType> getProducibleMediaTypes(
         HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
     ​
         // 获取请求域中数据
         Set<MediaType> mediaTypes =
             (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
         if (!CollectionUtils.isEmpty(mediaTypes)) {
             return new ArrayList<>(mediaTypes);
         }
         else if (!this.allSupportedMediaTypes.isEmpty()) {
             List<MediaType> result = new ArrayList<>();
             // 遍历所有的 HttpMessageConverter,看哪个能处理,就拿到它能支持的媒体类型放入集合中
             for (HttpMessageConverter<?> converter : this.messageConverters) {
                 if (converter instanceof GenericHttpMessageConverter && targetType != null) {
                     if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
                         result.addAll(converter.getSupportedMediaTypes());
                     }
                 }
                 else if (converter.canWrite(valueClass, null)) {
                     result.addAll(converter.getSupportedMediaTypes());
                 }
             }
             return result;
         }
         else {
             return Collections.singletonList(MediaType.ALL);
         }
     }

最终得到的媒体类型:*/* 意味着什么类型都可以接收

image.png

3、得到兼容的媒体类型

    List<MediaType> mediaTypesToUse = new ArrayList<>();

    // 得到可以使用的媒体类型
    for (MediaType requestedType : acceptableTypes) {
        for (MediaType producibleType : producibleTypes) {
            if (requestedType.isCompatibleWith(producibleType)) {
                mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
            }
        }
    }
    if (mediaTypesToUse.isEmpty()) {
        if (body != null) {
            throw new HttpMediaTypeNotAcceptableException(producibleTypes);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
        }
        return;
    }

    // 对媒体类型排序
    MediaType.sortBySpecificityAndQuality(mediaTypesToUse);

    // 得到最终选定的媒体类型
    for (MediaType mediaType : mediaTypesToUse) {
        // 判断是否是通配符类型
        if (mediaType.isConcrete()) {
            // 得到最终选定的媒体类型
            selectedMediaType = mediaType;
            break;
        }
        else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
            selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
            break;
        }
    }

最终选择的媒体类型:

image.png

4、利用HttpMessageConverter把数据写出去

    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        // 1、遍历所有 HttpMessageConverter
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                                                            (GenericHttpMessageConverter<?>) converter : null);
            // 2、判断这种 HttpMessageConverter 是否支持这种媒体类型
            if (genericConverter != null ?
                ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                converter.canWrite(valueType, selectedMediaType)) {
                // 3、ResponseBodyAdvice 类对返回值进行统一处理
                body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                                                   (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                                                   inputMessage, outputMessage);
                if (body != null) {
                    Object theBody = body;
                    LogFormatUtils.traceDebug(logger, traceOn ->
                                              "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                    addContentDispositionHeader(inputMessage, outputMessage);
                    if (genericConverter != null) {
                        // 4、利用 HttpMessageConverter 把数据写出去
                        genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                    }
                    else {
                        // 4、利用 HttpMessageConverter 把数据写出去
                        ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                    }
                }
                else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Nothing to write: null body");
                    }
                }
                return;
            }
        }
    }

流程

  1. 拿到最终确定的媒体类型:selectedMediaType

  2. 遍历所有HttpMessageConverter,进行处理

    1. 判断这种HttpMessageConverter是否支持这种媒体类型

    2. ResponseBodyAdvice类对返回值进行统一处理

    3. 利用HttpMessageConverter把返回值写出去

      1. 添加默认的响应头
      2. 利用ObjectMapper把数据写出去

最终拿到的是MappingJackson2HttpMessageConverter

image.png

write()

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

        final HttpHeaders headers = outputMessage.getHeaders();
        // 1、添加默认的响应头
        addDefaultHeaders(headers, t, contentType);

        if (outputMessage instanceof StreamingHttpOutputMessage) {
            StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
            streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
                @Override
                public OutputStream getBody() {
                    return outputStream;
                }
                @Override
                public HttpHeaders getHeaders() {
                    return headers;
                }
            }));
        }
        else {
            // 2、利用ObjectMapper把数据写出去
            writeInternal(t, type, outputMessage);
            // 刷新
            outputMessage.getBody().flush();
        }
    }

    // 利用ObjectMapper把数据写出去
    protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
          throws IOException, HttpMessageNotWritableException {

       // 获取 Content-Type 的值
       MediaType contentType = outputMessage.getHeaders().getContentType();
       // 编码
       JsonEncoding encoding = getJsonEncoding(contentType);

       OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
       // 拿到 Json 的生成器
       JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputStream, encoding);
       try {
          writePrefix(generator, object);

          Object value = object;
          Class<?> serializationView = null;
          FilterProvider filters = null;
          JavaType javaType = null;

          if (object instanceof MappingJacksonValue) {
             MappingJacksonValue container = (MappingJacksonValue) object;
             value = container.getValue();
             serializationView = container.getSerializationView();
             filters = container.getFilters();
          }
          if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
             javaType = getJavaType(type, null);
          }

           // 拿到 ObjectWriter
          ObjectWriter objectWriter = (serializationView != null ?
                this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
          if (filters != null) {
             objectWriter = objectWriter.with(filters);
          }
          if (javaType != null && javaType.isContainerType()) {
             objectWriter = objectWriter.forType(javaType);
          }
          SerializationConfig config = objectWriter.getConfig();
          if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
                config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
             objectWriter = objectWriter.with(this.ssePrettyPrinter);
          }
          // 写数据
          objectWriter.writeValue(generator, value);

          writeSuffix(generator, object);
          generator.flush();
          generator.close();
       }
       catch (InvalidDefinitionException ex) {
          throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
       }
       catch (JsonProcessingException ex) {
          throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
       }
    }

最终要写出去的数据

image.png

到这里我们的返回值就处理完毕了,最终就能得到一个json类型的返回值。

总结

流程

  1. 通过selectHandler()来选择一个HandlerMethodReturnValueHandler

    1. json的会由RequestResponseBodyMethodProcessor来处理
  2. 通过找到的返回值处理器来处理返回值

    1. 调用writeWithMessageConverters()利用HttpMessageConverter来处理
    2. 通过ContentNegotiationStrategy内容协商策略,来看客户端能接收什么媒体类型(默认获取请求头Accept中的内容)
    3. 根据解析的结果,选择最适合的媒体类型来响应客户端。(这可能涉及到优先级排序和质量因子(q值)的比较)
    4. 最后通过HttpMessageConverter来处理,它又通过JacksonObjectMapper类来处理

RequestResponseBodyMethodProcessor

这个类用来处理@RequestBody@ResponseBody注解

可以看到这个类实现了参数解析器返回值处理器

image.png

结构:

image.png

具体属性:

image.png

HandlerMethodReturnValueHandler

返回值处理器:用来处理返回值

    public interface HandlerMethodReturnValueHandler {
        
        // 判断当前的 HandlerMethodReturnValueHandler 是否支持处理给定类型的返回值
    	boolean supportsReturnType(MethodParameter returnType);

        // 处理返回值
    	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
    			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

    }

默认有15个返回值处理器

image.png

ContentNegotiationManager

内容协商管理器。内容协商是指服务器根据客户端请求头中提供的信息(如Accept头),决定返回哪种格式的数据的过程。

请求同一个接口,我要json就给我json格式,要xml就给我xml格式。这就是内容协商。用来做多端内容适配

image.png

各种实现

  • RequestResponseBodyMethodProcessor:处理@RequestBody@ResponseBody注解的方法。(最重要、最常用
  • ViewNameMethodReturnValueHandler:处理返回视图名称的情况。
  • ModelAttributeMethodProcessor:处理返回模型属性的情况。
  • ExceptionHandlerExceptionResolver:处理异常并返回适当的响应

HttpMessageConverter

用于处理 HTTP 请求和响应的消息体与 Java 对象之间的转换

这个接口定义了如何将 Java 对象序列化为 HTTP 消息体内容,以及如何将 HTTP 消息体内容反序列化为 Java 对象。Spring MVC 利用 HttpMessageConverter 实现了对各种数据格式(如 JSON、XML、CSV 等)的支持。

image.png

HttpMessageConverter接口主要包含以下几个方法:

  • boolean canRead(Class<?> clazz, MediaType mediaType):用于判断此转换器是否可以读取指定类型的对象,基于给定的媒体类型(MediaType)。返回 true 如果可以读取,否则返回 false。
  • boolean canWrite(Class<?> clazz, MediaType mediaType):用于判断此转换器是否可以写入指定类型的对象,同样基于给定的媒体类型。返回 true 如果可以写入,否则返回 false。
  • List<MediaType> getSupportedMediaTypes():返回此转换器支持的所有媒体类型列表。
  • Object read(Class<?> clazz, HttpInputMessage inputMessage):从 HTTP 输入消息中读取并转换为 Java 对象。抛出 IOException 或 HttpMessageNotReadableException 如果读取失败。
  • void write(Object obj, MediaType mediaType, HttpOutputMessage outputMessage):将 Java 对象写入到 HTTP 输出消息中。抛出 IOException 或 HttpMessageNotWritableException 如果写入失败

各种实现

  • MappingJackson2HttpMessageConverter:使用 Jackson 库进行 JSON 数据的读写。
  • ByteArrayHttpMessageConverter: 支持字节数据读写
  • StringHttpMessageConverter:支持字符串读写
  • FormHttpMessageConverter:处理表单数据的读写。
  • ResourceHttpMessageConverter:用于处理资源文件的读写
  • ResourceRegionHttpMessageConverter: 支持分区资源写出
  • AllEncompassingFormHttpMessageConverter:支持表单xml/json读写
  • MappingJackson2HttpMessageConverter: 支持请求响应体json读写

后面如果可以的话,会单独写文章为这些组件做介绍。