前面我們已經知道,解析request要找到參數解析器和返回值處理器,而對于@ResponseBody注解的方法,其實就是其對應的返回值處理器再起作用
返回值處理器,我們知道有默認15種 :
其中處理器RequestResponseBodyMethodHandler,就是我們使用@ResponseBody時,使用的處理器,底層如下:
那么拿到對應的返回值處理器后,springMVC是如何對返回值進行操作的呢?
debug發現,處理@ResponseBody,會調用以下方法:
writeWithMessageConverters部分源碼:
protected <T> void writeWithMessageConverters(@Nullable T value
, MethodParameter returnType
, ServletServerHttpRequest inputMessage
, ServletServerHttpResponse outputMessage
) throws IOException
, HttpMediaTypeNotAcceptableException
, HttpMessageNotWritableException
{Object body
;Class
valueType;Object targetType
;if (value
instanceof CharSequence) {body
= value
.toString();valueType
= String
.class;targetType
= String
.class;} else {body
= value
;valueType
= this.getReturnValueType(value
, returnType
);targetType
= GenericTypeResolver
.resolveType(this.getGenericType(returnType
), returnType
.getContainingClass());}if (this.isResourceType(value
, returnType
)) {outputMessage
.getHeaders().set("Accept-Ranges", "bytes");if (value
!= null
&& inputMessage
.getHeaders().getFirst("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 var19
) {outputMessage
.getHeaders().set("Content-Range", "bytes */" + resource
.contentLength());outputMessage
.getServletResponse().setStatus(HttpStatus
.REQUESTED_RANGE_NOT_SATISFIABLE
.value());}}}MediaType selectedMediaType
= null
;MediaType contentType
= outputMessage
.getHeaders().getContentType();boolean isContentTypePreset
= contentType
!= null
&& contentType
.isConcrete();if (isContentTypePreset
) {if (this.logger
.isDebugEnabled()) {this.logger
.debug("Found 'Content-Type:" + contentType
+ "' in response");}selectedMediaType
= contentType
;} else {HttpServletRequest request
= inputMessage
.getServletRequest();List
<MediaType> acceptableTypes
= this.getAcceptableMediaTypes(request
);List
<MediaType> producibleTypes
= this.getProducibleMediaTypes(request
, valueType
, (Type
)targetType
);if (body
!= null
&& producibleTypes
.isEmpty()) {throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType
);}List
<MediaType> mediaTypesToUse
= new ArrayList();Iterator var15
= acceptableTypes
.iterator();MediaType mediaType
;while(var15
.hasNext()) {mediaType
= (MediaType
)var15
.next();Iterator var17
= producibleTypes
.iterator();while(var17
.hasNext()) {MediaType producibleType
= (MediaType
)var17
.next();if (mediaType
.isCompatibleWith(producibleType
)) {mediaTypesToUse
.add(this.getMostSpecificMediaType(mediaType
, producibleType
));}}}if (mediaTypesToUse
.isEmpty()) {if (body
!= null
) {throw new HttpMediaTypeNotAcceptableException(producibleTypes
);}if (this.logger
.isDebugEnabled()) {this.logger
.debug("No match for " + acceptableTypes
+ ", supported: " + producibleTypes
);}return;}MediaType
.sortBySpecificityAndQuality(mediaTypesToUse
);var15
= mediaTypesToUse
.iterator();while(var15
.hasNext()) {mediaType
= (MediaType
)var15
.next();if (mediaType
.isConcrete()) {selectedMediaType
= mediaType
;break;}if (mediaType
.isPresentIn(ALL_APPLICATION_MEDIA_TYPES
)) {selectedMediaType
= MediaType
.APPLICATION_OCTET_STREAM
;break;}}if (this.logger
.isDebugEnabled()) {this.logger
.debug("Using '" + selectedMediaType
+ "', given " + acceptableTypes
+ " and supported " + producibleTypes
);}}HttpMessageConverter converter
;GenericHttpMessageConverter genericConverter
;label159
: {if (selectedMediaType
!= null
) {selectedMediaType
= selectedMediaType
.removeQualityValue();Iterator var22
= this.messageConverters
.iterator();while(var22
.hasNext()) {converter
= (HttpMessageConverter
)var22
.next();genericConverter
= converter
instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter
)converter
: null
;if (genericConverter
!= null
) {if (((GenericHttpMessageConverter
)converter
).canWrite((Type
)targetType
, valueType
, selectedMediaType
)) {break label159
;}} else if (converter
.canWrite(valueType
, selectedMediaType
)) {break label159
;}}}
}
0 - 只支持Byte類型的
1 - String
2 - String
3 - Resource
4 - ResourceRegion
5 - DOMSource
.class \ SAXSource
.class) \ StAXSource
.class \StreamSource.class \Source
.class
6 - MultiValueMap
7 - true
8 - true
9 - 支持注解方式xml處理的。
?
1、返回值處理器判斷是否支持這種類型返回值 supportsReturnType
?
2、返回值處理器調用 handleReturnValue 進行處理
?
3、RequestResponseBodyMethodProcessor 可以處理返回值標了
@ResponseBody 注解的。? 利用 MessageConverters 進行處理 將數據寫為json? A
.內容協商(瀏覽器默認會以請求頭的方式告訴服務器他能接受什么樣的內容類型)? B
.服務器最終根據自己自身的能力,決定服務器能生產出什么樣內容類型的數據,? C
.SpringMVC會挨個遍歷所有容器底層的 HttpMessageConverter ,看誰能處理?? a
.得到MappingJackson2HttpMessageConverter可以將對象寫為json? b
.利用MappingJackson2HttpMessageConverter將對象轉為json再寫出去。
- 附加:文件返回方式
FileSystemResource 是Resource的實現類,所以springMVC最終會調用對應的messageConverter進行處理
總結
以上是生活随笔為你收集整理的SpringBoot对于标注@ResponseBody注解返回JSON数据的处理的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。