SpringMVC返回值处理器 ReturnValueHandler(五)

SpringMVC默认的返回值处理器有:

0 = {ModelAndViewMethodReturnValueHandler@8543} 
1 = {ModelMethodProcessor@8546} 
2 = {ViewMethodReturnValueHandler@8547} 
3 = {ResponseBodyEmitterReturnValueHandler@8548} 
4 = {StreamingResponseBodyReturnValueHandler@8549} 
5 = {HttpEntityMethodProcessor@8550} 
6 = {HttpHeadersReturnValueHandler@8551} 
7 = {CallableMethodReturnValueHandler@8552} 
8 = {DeferredResultMethodReturnValueHandler@8553} 
9 = {AsyncTaskMethodReturnValueHandler@8554} 
10 = {ServletModelAttributeMethodProcessor@8162} 
11 = {RequestResponseBodyMethodProcessor@8555} 
12 = {ViewNameMethodReturnValueHandler@8556} 
14 = {MapMethodProcessor@8557} 
14 = {ServletModelAttributeMethodProcessor@8558} 

我们最常用的就是 Controller + 返回值"String"Controller + 返回值+ @ResponseBody,现在分析一下,这两种情况是如何处理的。

Controller + 返回值”String”

@Controller
public class TestController {

    @GetMapping("index")
    public String toPage() {
        return "index";
    }
}
GET http://localhost:8080/index

会由 ViewNameMethodReturnValueHandler 去处理,将返回值放到ModeAndView的viewName中。这样视图解析器就会去找相应的视图。

Controller + 返回值+ @ResponseBody

RestController + 返回值 = Controller + 返回值+ @ResponseBody

GET http://localhost:8080/index

会由 RequestResponseBodyMethodProcessor 去处理,看下这个解析器的源码

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    // Try even with null return value. ResponseBodyAdvice could get involved.
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

做2件事情:

  • 设置setRequestHandled(true) 告诉视图解析器,已经处理过了,不需要渲染
  • 找到合适的MessageConverter,然后将值直接写出。

RequestResponseBodyMethodProcessor 在处理的过程中,客户端可以使用注解@ControllerAdvice在write之前对值进行处理

@ControllerAdvice
public class MyAdvice implements ResponseBodyAdvice {
@Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        System.out.println("ResponseBodyAdvice: 执行 " + o);
        return o;
    }}

自定义HandlerMethodReturnValueHandler

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        handlers.add(new HandlerMethodReturnValueHandler() {

            @Override
            public boolean supportsReturnType(MethodParameter returnType) {
                return Integer.class == returnType.getParameterType();
            }

            @Override
            public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
                Integer value = (Integer) returnValue;
                mavContainer.addAttribute("intValue", value);
                if (value > 10) {
                    mavContainer.setViewName("big");
                } else {
                    mavContainer.setViewName("small");
                }
            }
        });
    }
}

自定义的HandlerMethodReturnValueHandler,做了2件事。

  • 将值放入Model中
  • 如果大于10跳转big.html,否则跳转small.html
@GetMapping("int/{intValue}")
public int toIntPage(@PathVariable Integer intValue) {
    return intValue;
}
GET http://localhost:8080/int/11

big.html

<body>
big.html
<h1 th:text="${intValue}"></h1>
</body>