关于@ResponseBody 默认输出的误区的解答

所属分类: 软件编程 / java 阅读数: 40
收藏 0 赞 0 分享

背景

@ResponseBody 默认情况返回的数据格式是什么?所谓默认情况 后台接口不指定 produces MediaType

@Controller
public class DemoController {
 @ResponseBody
 @GetMapping(value = "/demo")
 public DemoVO demo() {
  return new DemoVO("lengleng", "123456");
 }
}

使用百度搜索 @ResponseBody 排名第一的答案, @ResponseBody 的作用其实是将 java 对象转为 json 格式的数据。

正确答案

我们先来公布正确的答案。

@ResponseBody 的输出格式,默认情况取决于客户端的 Accept 请求头。

源码剖析

RequestResponseBodyMethodProcessor

public class RequestResponseBodyMethodProcessor {
// 处理 ResponseBody 标注的方法
@Override
public boolean supportsReturnType(MethodParameter returnType) {
  return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
    returnType.hasMethodAnnotation(ResponseBody.class));
 }
// 处理返回值
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
               ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
  mavContainer.setRequestHandled(true);
  ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
  ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
  // 处理返回值
  writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
 }
}

writeWithMessageConverters

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {
 HttpServletRequest request = inputMessage.getServletRequest();
 // 获取请求头中的目标资源类型
 List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
 // 获取接口指定支持的资源类型
 List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
 // 获取能够输出资源类型
 List<MediaType> mediaTypesToUse = new ArrayList<>();
 for (MediaType requestedType : acceptableTypes) {
  for (MediaType producibleType : producibleTypes) {
   if (requestedType.isCompatibleWith(producibleType)) {
    mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
   }
  }
 }
 /// 排序
 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;
  }
 }

 selectedMediaType = selectedMediaType.removeQualityValue();
 // 查找支持选中资源类型的 HttpMessageConverter,输出body
 for (HttpMessageConverter<?> converter : this.messageConverters) {
  GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
   (GenericHttpMessageConverter<?>) converter : null);
  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);
   return;
  }
 }
}

为什么我要去研究这个问题

当升级至 spring cloud alibaba 2.2.1 时, sentinel 模块 引入以下依赖

当依赖中出现 dataformat jar 时候, RestTemplate ,会在默认 Accept 请求头增加

application/xml | text/xml | application/*+xml

public MappingJackson2XmlHttpMessageConverter(ObjectMapper objectMapper) {
 super(objectMapper, new MediaType("application", "xml", StandardCharsets.UTF_8),
   new MediaType("text", "xml", StandardCharsets.UTF_8),
   new MediaType("application", "*+xml", StandardCharsets.UTF_8));
 Assert.isInstanceOf(XmlMapper.class, objectMapper, "XmlMapper required");
}

当我们使用 RestTemplate 调用接口时候,若不指定 Accept 会返回 XML ,导致不能平滑升级

更多精彩内容其他人还在看

SpringBoot中使用Ehcache的详细教程

EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。这篇文章主要介绍了SpringBoot中使用Ehcache的相关知识,需要的朋友可以参考下
收藏 0 赞 0 分享

在idea 中添加和删除模块Module操作

这篇文章主要介绍了在idea 中添加和删除模块Module操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
收藏 0 赞 0 分享

java spring整合junit操作(有详细的分析过程)

这篇文章主要介绍了java spring整合junit操作(有详细的分析过程),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
收藏 0 赞 0 分享

详解JAVA 弱引用

这篇文章主要介绍了 JAVA 弱引用的相关资料,帮助大家更好的理解和学习java引用对象,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

深入了解JAVA 虚引用

这篇文章主要介绍了JAVA 虚引用的相关资料,帮助大家更好的理解和学习JAVA,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

详解JAVA 强引用

这篇文章主要介绍了JAVA 强引用的相关资料,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

java中的按位与(&)用法说明

这篇文章主要介绍了java中的按位与(&)用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
收藏 0 赞 0 分享

深入了解JAVA 软引用

这篇文章主要介绍了JAVA 软引用的相关资料,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

利用MyBatis实现条件查询的方法汇总

这篇文章主要给大家介绍了关于利用MyBatis实现条件查询的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用MyBatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
收藏 0 赞 0 分享

Intellij IDEA 与maven 版本不符 Unable to import maven project See logs for details: No implementation for org.apache.maven.model.path.PathTranslator was bound

这篇文章主要介绍了Intellij IDEA 与maven 版本不符 Unable to import maven project See logs for details: No implementation for org.apache.maven.model.path.Pa
收藏 0 赞 0 分享
查看更多