- 前面我们已经知道,解析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();
//遍历所有的HttpMessageConverter(看是否支持将 此 Class类型的对象,转为MediaType类型的数据,过程是可逆的)
//得到可匹配的消息转换器,支持将对象转成Json数据,这就是关键的核心
//最终得到MappingJackson2HttpMessageConverter可以将对象写为json
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;
}
}
}
}
- 最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的
- MessageConverter规范
- 默认的MessageConverter
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
进行处理