慚愧!原來我一直沒用明白 @ResponseBody註解

作者:冷冷gg 鏈接:https://juejin.im/post/5e93f6b6e51d4546f27ff50a

背景

  • @ResponseBody 默認情況返回的數據格式是什麼?所謂默認情況 後臺接口不指定 produces MediaType
<code>@Controller
public class DemoController {
  @ResponseBody
  @GetMapping(value = "/demo")
  public DemoVO demo() {
    return new DemoVO("lengleng", "123456");
  }
}/<code>
  • 使用百度搜索 @ResponseBody 排名第一的答案, @ResponseBody 的作用其實是將 java 對象轉為 json 格式的數據。
慚愧!原來我一直沒用明白 @ResponseBody註解

正確答案

我們先來公佈正確的答案。

@ResponseBody 的輸出格式,默認情況取決於客戶端的 Accept 請求頭。

慚愧!原來我一直沒用明白 @ResponseBody註解

慚愧!原來我一直沒用明白 @ResponseBody註解

源碼剖析

  • RequestResponseBodyMethodProcessor
<code>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);
  }
}/<code>
  • writeWithMessageConverters
<code>protected  void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
                        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {
  HttpServletRequest request = inputMessage.getServletRequest();
  // 獲取請求頭中的目標資源類型
  List acceptableTypes = getAcceptableMediaTypes(request);
  // 獲取接口指定支持的資源類型
  List producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
  // 獲取能夠輸出資源類型
  List 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; } } }/<code>


為什麼我要去研究這個問題

  • 當升級至 spring cloud alibaba 2.2.1 時, sentinel 模塊 引入以下依賴
慚愧!原來我一直沒用明白 @ResponseBody註解

  • 當依賴中出現 dataformat jar 時候, RestTemplate ,會在默認 Accept 請求頭增加

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

慚愧!原來我一直沒用明白 @ResponseBody註解

<code>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");
}/<code>
  • 當我們使用 RestTemplate 調用接口時候,若不指定 Accept 會返回 XML ,導致不能平滑升級

感悟

從正式成為一名程序員的那天起,註定要進行沒有止境的學習,想要進階高級或者專家,就要堅持每天都高效的學習,不要給自己的懶惰找藉口,“什麼我也想學習可是又沒有資源”,這次我給你整理好了,我看你還有啥理由!

私信回覆【666】送你

慚愧!原來我一直沒用明白 @ResponseBody註解

慚愧!原來我一直沒用明白 @ResponseBody註解


分享到:


相關文章: