SpringMvc源码分析6——HandlerMethodReturnValueHandler


回到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

returnValueHandlers

然后看一下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方法调用返回的值。

接口的实现:

HandlerMethodReturnValueHandler

这些策略实现,使的程序方便的处理各种返回值,比如StringModelAndViewMap,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.

官方文档列的也比较详细,但是有一点还是有必要说明一下,就是当使用@ResponseBodyHttpEntity, ResponseEntity时候,我们可以利用ResponseBodyAdviceHttpMessageConverter#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;
    }

这样就可以在返回值处理之前实现自己的处理逻辑。

二. 总结

  1. springMvc会使用returnValueHandlers来处理handler的调用的返回值;
  2. springMvc支持的controller方法返回值有哪些;
  3. ResponseBodyAdvice的作用,以及如何自定义ResponseBodyAdvice;

文章作者: shiv
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 shiv !
评论
  目录