回到SpringMvc源码分析5.1——HandlerMethodArgumentResolver的源码分析ServletInvocableHandlerMethod#invokeAndHandle(..)
:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 重点,从请求上下文中解析出参数值,然后调用方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置响应状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 重点关注,调用匹配的HandlerMethodReturnValueHandler
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
对于参数解析的那一块我们前面已经分析过,所以我们接着分析handleReturnValue(..)
返回值处理这一块。
一. 源码分析
追踪一下
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest)
方法
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 遍历找到支持的HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
遍历已经注册的HandlerMethodReturnValueHandler
,找到匹配的并执行它的handleReturnValue(..)
。
首先我们看一下已经注册的HandlerMethodReturnValueHandler
:
然后看一下HandlerMethodReturnValueHandler
的接口定义:
public interface HandlerMethodReturnValueHandler {
/**
* Whether the given {@linkplain MethodParameter method return type} is
* supported by this handler.
* @param returnType the method return type to check
* @return {@code true} if this handler supports the supplied return type;
* {@code false} otherwise
*/
boolean supportsReturnType(MethodParameter returnType);
/**
* Handle the given return value by adding attributes to the model and
* setting a view or setting the
* {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
* to indicate the response has been handled directly.
* @param returnValue the value returned from the handler method
* @param returnType the type of the return value. This type must have
* previously been passed to {@link #supportsReturnType} which must
* have returned {@code true}.
* @param mavContainer the ModelAndViewContainer for the current request
* @param webRequest the current request
* @throws Exception if the return value handling results in an error
*/
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
策略接口,用于处理从
handler
方法调用返回的值。
接口的实现:
这些策略实现,使的程序方便的处理各种返回值,比如String
,ModelAndView
,Map
,HttpEntity
,@RequestBody
注解等等。那我SpringMvc
支持那些返回值呢,看一下官方文档:
Controller method return value | Description |
---|---|
@ResponseBody |
The return value is converted through HttpMessageConverter implementations and written to the response. See @ResponseBody . |
HttpEntity , ResponseEntity |
The return value that specifies the full response (including HTTP headers and body) is to be converted through HttpMessageConverter implementations and written to the response. See ResponseEntity. |
HttpHeaders |
For returning a response with headers and no body. |
String |
A view name to be resolved with ViewResolver implementations and used together with the implicit model — determined through command objects and @ModelAttribute methods. The handler method can also programmatically enrich the model by declaring a Model argument (see Explicit Registrations). |
View |
A View instance to use for rendering together with the implicit model — determined through command objects and @ModelAttribute methods. The handler method can also programmatically enrich the model by declaring a Model argument (see Explicit Registrations). |
java.util.Map , org.springframework.ui.Model |
Attributes to be added to the implicit model, with the view name implicitly determined through a RequestToViewNameTranslator . |
@ModelAttribute |
An attribute to be added to the model, with the view name implicitly determined through a RequestToViewNameTranslator .Note that @ModelAttribute is optional. See “Any other return value” at the end of this table. |
ModelAndView object |
The view and model attributes to use and, optionally, a response status. |
void |
A method with a void return type (or null return value) is considered to have fully handled the response if it also has a ServletResponse , an OutputStream argument, or an @ResponseStatus annotation. The same is also true if the controller has made a positive ETag or lastModified timestamp check (see Controllers for details).If none of the above is true, a void return type can also indicate “no response body” for REST controllers or a default view name selection for HTML controllers. |
DeferredResult |
Produce any of the preceding return values asynchronously from any thread — for example, as a result of some event or callback. See Asynchronous Requests and DeferredResult . |
Callable |
Produce any of the above return values asynchronously in a Spring MVC-managed thread. See Asynchronous Requests and Callable . |
ListenableFuture , java.util.concurrent.CompletionStage , java.util.concurrent.CompletableFuture |
Alternative to DeferredResult , as a convenience (for example, when an underlying service returns one of those). |
ResponseBodyEmitter , SseEmitter |
Emit a stream of objects asynchronously to be written to the response with HttpMessageConverter implementations. Also supported as the body of a ResponseEntity . See Asynchronous Requests and HTTP Streaming. |
StreamingResponseBody |
Write to the response OutputStream asynchronously. Also supported as the body of a ResponseEntity . See Asynchronous Requests and HTTP Streaming. |
Reactive types — Reactor, RxJava, or others through ReactiveAdapterRegistry |
Alternative to DeferredResult with multi-value streams (for example, Flux , Observable ) collected to a List .For streaming scenarios (for example, text/event-stream , application/json+stream ), SseEmitter and ResponseBodyEmitter are used instead, where ServletOutputStream blocking I/O is performed on a Spring MVC-managed thread and back pressure is applied against the completion of each write.See Asynchronous Requests and Reactive Types. |
Any other return value | Any return value that does not match any of the earlier values in this table and that is a String or void is treated as a view name (default view name selection through RequestToViewNameTranslator applies), provided it is not a simple type, as determined by BeanUtils#isSimpleProperty. Values that are simple types remain unresolved. |
官方文档列的也比较详细,但是有一点还是有必要说明一下,就是当使用@ResponseBody
,HttpEntity
, ResponseEntity
时候,我们可以利用ResponseBodyAdvice
在HttpMessageConverter#write(..)
前做一些小处理,源码位置在AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)
:
...
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)) {
// 利用ResponseBodyAdvice做一些提前处理
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
...
定义ResponseBodyAdvice
的方式,如下:
@ControllerAdvice
public class ResponseBodyAdviceExample implements ResponseBodyAdvice<String> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// todo
return false;
}
@Override
public String beforeBodyWrite(String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// todo
return null;
}
这样就可以在返回值处理之前实现自己的处理逻辑。
二. 总结
springMvc
会使用returnValueHandlers
来处理handler
的调用的返回值;springMvc
支持的controller
方法返回值有哪些;ResponseBodyAdvice
的作用,以及如何自定义ResponseBodyAdvice
;