参数的返回
经过参数绑定、参数验证之后,调用controller方法,获得返回结果。
InvocableHandlerMethod->invokeForRequest():
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
return doInvoke(args);
返回参数的处理
在参数绑定的准备工作中,ServletInvocableHandlerMethod的resolvers负责请求参数的处理,同样,它的returnValueHandlers负责返回参数的处理。
ServletInvocableHandlerMethod->invokeAndHandle():
// 这里的returnValue即为返回值,第二个参数是返回值的类型
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
HandlerMethodReturnValueHandlerComposite->handleReturnValue():
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
不同的参数+特定的注解会得到不同的解析器,后面绑定参数也不尽相同,下面会对常用的参数类型作举例,至于其他的可以自行研究
RequestResponseBodyMethodProcessor
对应的是方法或类存在@ResponseBody注解的情况,最常用的是@RestController注解,当然也可以像这样写@ResponseBody public String test1(Tree tree)),这是SpringBoot中使用最多的一种解析器
RequestResponseBodyMethodProcessor->handleReturnValue():
// 告诉MVC后面不用再跳转视图了
mavContainer.setRequestHandled(true);
......
// 调用的是其父类的writeWithMessageConverters()方法
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
AbstractMessageConverterMethodProcessor->writeWithMessageConverters():
// 获得请求信息头部的accpet信息
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
// 从消息转换器(messageConverters)遍历,如果它支持返回值类型的写入,会将其下的所有媒体类型添加
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));
}
}
}
// 之后mediaTypesToUse排序,得到最符合的媒体类型
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
// 确定返回要使用哪种媒体类型
// 再次从messageConverters遍历,找到同时支持返回值类型与媒体类型的消息转换器
......
if (body != null) {
// 以流的形式写入返回数据
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
} else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
return;
}
不同的参数类型会返回不同的媒体类型,使用的消息转换器也不一样,简单举例(假设没有使用阿里的FastJson,因为一旦使用,消息转换器默认为FastJsonHttpMessageConverter):
| 参数类型 | 媒体类型 | 消息转换器 | 呈现 |
|---|---|---|---|
| 基本类型与其包装类 | application/json | MappingJackson2HttpMessageConverter | 基本类型 |
| 类类型 | application/json | MappingJackson2HttpMessageConverter | json格式 |
| String | text/html | StringHttpMessageConverter | 字符串 |
xml(类中存在@XmlRootElement注解) | application/xhtml+xml | Jaxb2RootElementHttpMessageConverter | xml格式 |
ViewNameMethodReturnValueHandler
对应返回值为String类型,添加返回值为视图名称。如果以redirect:开头,会重定向(redirectModelScenario设为true),其他不做处理
ViewNameMethodReturnValueHandler->handleReturnValue():
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
ModelAttributeMethodProcessor
对应返回值为类类型,不做处理
ModelAndViewMethodReturnValueHandler
对应返回值为ModelAndView类,与ViewNameMethodReturnValueHandler的处理方法类似