spring boot · 2024-09-05 0

springboot 自定义 HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler

一、使用

1.pom

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.2</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

2.java

@Setter
@Getter
@ToString
@AllArgsConstructor
public class Pet {

    private Integer id;

    private String name;
}

自定义 HandlerMethodArgumentResolver

public class PetArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(Pet.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Integer id = Integer.parseInt(webRequest.getParameter("id"));
        String name = webRequest.getParameter("name");
        return new Pet(id, name);
    }
}

自定义 HandlerMethodReturnValueHandler

public class PetReturnValueHandler implements HandlerMethodReturnValueHandler {

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

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest) throws Exception {
        mavContainer.setRequestHandled(true);

        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        String msg = String.format("code: 200 data: %s", returnValue);

        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.write(msg);
            writer.flush();
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}
@Configuration(proxyBeanMethods = false)
public class PetWebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new PetArgumentResolver());
    }

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

IndexController

@Controller
public class IndexController {

    @GetMapping("/t1")
    public Pet t1(Pet pet) {
        return pet;
    }
}

3.测试

zxm@zxm-pc:~$ curl -X GET "http://localhost:8080/t1?id=123&name=zhangsan"
code: 200 data: Pet(id=123, name=zhangsan)

二、默认

处理请求参数:

序号 类名 功能
1 RequestParamMethodArgumentResolver(beanFactory, false) 处理 @RequestParam
2 RequestParamMapMethodArgumentResolver 处理 @RequestParam Map
3 PathVariableMethodArgumentResolver 处理 @PathVariable
4 PathVariableMapMethodArgumentResolver 处理 @PathVariable Map
5 MatrixVariableMethodArgumentResolver 处理 @MatrixVariable
6 MatrixVariableMapMethodArgumentResolver 处理 @MatrixVariable Map
7 ServletModelAttributeMethodProcessor(false) 处理 @ModelAttribute
8 RequestResponseBodyMethodProcessor 处理 @RequestBody
9 RequestPartMethodArgumentResolver 处理 @RequestPart
10 RequestHeaderMethodArgumentResolver 处理 @RequestHeader
11 RequestHeaderMapMethodArgumentResolver 处理 @RequestHeader Map
12 ServletCookieValueMethodArgumentResolver 处理 @CookieValue
13 ExpressionValueMethodArgumentResolver 处理 @Value
14 SessionAttributeMethodArgumentResolver 处理 @SessionAttribute
15 RequestAttributeMethodArgumentResolver 处理 @RequestAttribute
16 ServletRequestMethodArgumentResolver 处理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
17 ServletResponseMethodArgumentResolver 处理 ServletResponse、OutputStream、Writer
18 HttpEntityMethodProcessor 处理 HttpEntity、RequestEntity
19 RedirectAttributesMethodArgumentResolver 处理 RedirectAttributes
20 ModelMethodProcessor 处理 Model
21 MapMethodProcessor 处理 Map
22 ErrorsMethodArgumentResolver 处理 Errors
23 SessionStatusMethodArgumentResolver 处理 SessionStatus
24 UriComponentsBuilderMethodArgumentResolver 处理 UriComponentsBuilder、ServletUriComponentsBuilder
25 PrincipalMethodArgumentResolver 处理 Principal
26 RequestParamMethodArgumentResolver(beanFactory, true) 处理 @RequestParam 或 isSimpleProperty(prameterType)
27 ServletModelAttributeMethodProcessor(true) 处理 @ModelAttribute 或 !isSimpleProperty(prameterType)

处理返回值:

序号 类名 功能
1 ModelAndViewMethodReturnValueHandler 处理 ModelAndView
2 ModelMethodProcessor 处理 Model
3 ViewMethodReturnValueHandler 处理 View
4 ResponseBodyEmitterReturnValueHandler 处理 ResponseEntity
5 StreamingResponseBodyReturnValueHandler 处理 StreamingResponseBody
6 HttpEntityMethodProcessor 处理 HttpEntity
7 HttpHeadersReturnValueHandler 处理 HttpHeaders
8 CallableMethodReturnValueHandler 处理 Callable
9 DeferredResultMethodReturnValueHandler 处理 DeferredResult
10 AsyncTaskMethodReturnValueHandler 处理 WebAsyncTask
11 ServletModelAttributeMethodProcessor(false) 处理 @ModelAttribute
12 RequestResponseBodyMethodProcessor 处理 @RequestBody
13 ViewNameMethodReturnValueHandler 处理 void,String
14 MapMethodProcessor 处理 Map
15 ServletModelAttributeMethodProcessor(true) @ModelAttribute 或 !isSimpleProperty(prameterType)

1.RequestParamMethodArgumentResolver(beanFactory, false) 处理 @RequestParam

// curl -X GET http://localhost:8080/param1_1?name1=zhangsan
// RequestParamMethodArgumentResolver(beanFactory, false)
@GetMapping("/param1_1")
public String param1_1(@RequestParam String name1) {
    return "param1_1";
}

// curl -X GET http://localhost:8080/param1_2?id1=1,2,3
// RequestParamMethodArgumentResolver(beanFactory, false)
@GetMapping("/param1_2")
public String param1_2(@RequestParam List<String> id1) {
    return "param1_2";
}

2.RequestParamMapMethodArgumentResolver 处理 @RequestParam Map

// curl -X GET "http://localhost:8080/param2?id1=123&name1=zhangsan"
// RequestParamMapMethodArgumentResolver
@GetMapping("/param2")
public String param2(@RequestParam Map<String, String> map) {
    return "param2";
}

3.PathVariableMethodArgumentResolver 处理 @PathVariable

// curl -X GET "http://localhost:8080/param3_1/123"
// PathVariableMethodArgumentResolver
@GetMapping("/param3_1/{id1}")
public String param3_1(@PathVariable String id1) {
    return "param3_1";
}

// curl -X GET "http://localhost:8080/param3_2/1,2,3"
// PathVariableMethodArgumentResolver
@GetMapping("/param3_2/{id1}")
public String param3_2(@PathVariable List<String> id1) {
    return "param3_2";
}

4.PathVariableMapMethodArgumentResolver 处理 @PathVariable Map

// curl -X GET "http://localhost:8080/param4/123/zhangsan"
// PathVariableMapMethodArgumentResolver
@GetMapping("/param4/{id1}/{name1}")
public String param4(@PathVariable Map<String, String> map) {
    return "param4";
}

5.MatrixVariableMethodArgumentResolver 处理 @MatrixVariable

// curl -X GET "http://localhost:8080/param5/pet;name1=zhangsan"
// MatrixVariableMethodArgumentResolver
@GetMapping("/param5/{obj}")
public String param5(@PathVariable String obj, @MatrixVariable(name = "name1") String name1) {
    return "param5";
}

6.MatrixVariableMapMethodArgumentResolver 处理 @MatrixVariable Map

// curl -X GET "http://localhost:8080/param6/pet;id1=123;name1=zhangsan"
// MatrixVariableMapMethodArgumentResolver
@GetMapping("/param6/{obj}")
public String param6(@PathVariable String obj, @MatrixVariable Map<String, String> map) {
    return "param6";
}

7.ServletModelAttributeMethodProcessor(false) 处理 @ModelAttribute

// curl -X GET "http://localhost:8080/param7_1?id1=123&name1=zhangsan"
// ServletModelAttributeMethodProcessor(false)
@GetMapping("/param7_1")
public String param7_1(@ModelAttribute(name = "id1") String id1) {
    return "param7_1";
}

// curl -X GET "http://localhost:8080/param7_2?id1=123&name1=zhangsan"
// ServletModelAttributeMethodProcessor(false)
@GetMapping("/param7_2")
public String param7_2(@ModelAttribute Pet pet) {
    return "param7_2";
}
// RequestMappingHandlerAdapter#invokeHandlerMethod() 方法中,ModelFactory#initModel() 会调用被 @ModelAttribute 标注的方法
// 在所有的请求处理方法执行之前,都会执行 @ModelAttribute 标注的方法
// 如果 @ModelAttribute 设置 value,会往 model 放入的,key 是 @ModelAttribute 的 value 值
// 如果 @ModelAttribute 不设置 value,会往 model 放入的, key 返回类型
@ModelAttribute(name = "attribute7_4")
public String attribute7_4(Model model) {
    model.addAttribute("id1", "123");
    model.addAttribute("name1", "zhangsan");
    return "this is fillData";
}

// curl -X GET "http://localhost:8080/param7_4"
// ServletModelAttributeMethodProcessor
// @ModelAttribute 还有一种用途是作为请求处理方法的参数,如果你需要 Model 中某个对象,可以给参数添加 @ModelAttribute 注解
@GetMapping("/param7_4")
public String param7_4(@ModelAttribute(name = "id1") String id1) {
    return "param7_4";
}

8.RequestResponseBodyMethodProcessor 处理 @RequestBody

// curl -X GET -d '{"id1":"123","name1":"zhangsan"}' "http://localhost:8080/param8_1"
// RequestResponseBodyMethodProcessor 使用 StringHttpMessageConverter 解析请求体
@GetMapping("/param8_1")
public String param8_1(@RequestBody String body) {
    return "param8_1";
}

// curl -X GET -H 'Content-Type: application/json' -d '{"id1":"123","name1":"zhangsan"}' "http://localhost:8080/param8_2"
// RequestResponseBodyMethodProcessor 使用 MappingJackson2HttpMessageConverter 解析请求体
@GetMapping("/param8_2")
public String param8_2(@RequestBody Pet pet) {
    return "param8_2";
}

9.RequestPartMethodArgumentResolver 处理 @RequestPart

// curl -X GET -F "file=@/home/zxm/file.txt" "http://localhost:8080/param9"
// RequestPartMethodArgumentResolver
@GetMapping("/param9")
public String param9(@RequestPart MultipartFile file) {
    return "param9";
}

10.RequestHeaderMethodArgumentResolver 处理 @RequestHeader

// curl -X GET -H 'head1:aa' -H 'head2:bb' "http://localhost:8080/param10"
// RequestHeaderMethodArgumentResolver
@GetMapping("/param10")
public String param10(@RequestHeader String head1) {
    return "param10";
}

11.RequestHeaderMapMethodArgumentResolver(false) 处理 @RequestHeader Map

// curl -X GET -H 'head1:aa' -H 'head2:bb' "http://localhost:8080/param11"
// RequestHeaderMapMethodArgumentResolver(false)
@GetMapping("/param11")
public String param11(@RequestHeader Map<String, String> map) {
    return "param11";
}

12.ServletCookieValueMethodArgumentResolver 处理 @CookieValue

// curl -X GET -H 'Cookie: cookie1=abc' -H 'Cookie: cookie2=def' "http://localhost:8080/param12_1"
// ServletCookieValueMethodArgumentResolver
@GetMapping("/param12_1")
public String param12_1(@CookieValue String cookie1) {
    return "param12_1";
}

// curl -X GET -H 'Cookie: cookie1=abc' -H 'Cookie: cookie2=def' "http://localhost:8080/param12_2"
// ServletCookieValueMethodArgumentResolver
@GetMapping("/param12_2")
public String param12_2(@CookieValue Cookie cookie1) {
    return "param12_2";
}

13.ExpressionValueMethodArgumentResolver 处理 @Value

// curl -X GET "http://localhost:8080/param13"
// application.yml 配置 name
// ExpressionValueMethodArgumentResolver
@GetMapping("/param13")
public String param13(@Value(value = "${name1}") String name) {
    return "param13";
}

14.SessionAttributeMethodArgumentResolver 处理 @SessionAttribute

// curl -v -X GET "http://localhost:8080/param16"
// ServletRequestMethodArgumentResolver
@GetMapping("/param16")
public String param16(HttpSession session) {
    session.setAttribute("id1", "123");;
    session.setAttribute("name1", "zhangsan");;
    return "param16";
}

// curl --cookie "JSESSIONID=457B69F762303CED4D49914F08090D6C" "http://localhost:8080/param14"
// SessionAttributeMethodArgumentResolver
@GetMapping("/param14")
public String param14(@SessionAttribute(name = "id1") String id1, @SessionAttribute(name = "name1") String name1) {
    return "param14";
}

15.RequestAttributeMethodArgumentResolver 处理 @RequestAttribute

// curl -X GET "http://localhost:8080/param16"
// ServletRequestMethodArgumentResolver
@GetMapping("/param16")
public String param16(HttpServletRequest request) {
    request.setAttribute("id1", "123");;
    request.setAttribute("name1", "zhangsan");;
    //转发到  /param15 请求
    return "forward:/param15";
}

// RequestAttributeMethodArgumentResolver
@GetMapping("/param15")
public String param15(@RequestAttribute(name = "id1") String id1, @RequestAttribute(name = "name1") String name1) {
    return "param15";
}

16.ServletRequestMethodArgumentResolver 处理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId

// curl -X GET "http://localhost:8080/param16"
// ServletRequestMethodArgumentResolver
@GetMapping("/param16")
public String param16(HttpMethod httpMethod, Locale locale, TimeZone timeZone, ZoneId zoneId) {
    return "param16";
}

17.ServletResponseMethodArgumentResolver 处理 ServletResponse、OutputStream、Writer

// curl -X GET "http://localhost:8080/param17_1"
// ServletResponseMethodArgumentResolver
// ServletResponseMethodArgumentResolver 会把设置 mavContainer.setRequestHandled(true)
// 使得不会查找 view,渲染 view
@GetMapping("/param17_1")
public void param17_1(Writer writer) throws IOException, InterruptedException {
    for (int i = 0; i < 10; i++) {
        TimeUnit.SECONDS.sleep(1);
        String data = "Data chunk " + i + "\n";
        writer.write(data);
        writer.flush();
    }
    writer.close();
}

// curl -X GET "http://localhost:8080/param17_2"
// ServletResponseMethodArgumentResolver
// ServletResponseMethodArgumentResolver 会把设置 mavContainer.setRequestHandled(true)
// 使得不会查找 view,渲染 view
@GetMapping("/param17_2")
public void param17_2(OutputStream outputStream) throws IOException, InterruptedException {
    for (int i = 0; i < 10; i++) {
        TimeUnit.SECONDS.sleep(1);
        String data = "Data chunk " + i + "\n";
        outputStream.write(data.getBytes());
        outputStream.flush();
    }
    outputStream.close();
}

18.HttpEntityMethodProcessor 处理 HttpEntity、RequestEntity

// curl -X GET "http://localhost:8080/param18"
// HttpEntityMethodProcessor
@GetMapping("/param18")
public String param18(HttpEntity httpEntity, RequestEntity requestEntity) {
    return "param18";
}

19.RedirectAttributesMethodArgumentResolver 处理 RedirectAttributes

// curl -v -X GET "http://localhost:8080/param19_1"
// RedirectAttributesMethodArgumentResolver
@GetMapping("/param19_1")
public String param19_1(RedirectAttributes redirectAttribute) {
    // redirectAttribute 是 ModelAndViewContainer 的 redirectModel
    // 这种方法相当于在重定向链接地址追加传递的参数。以上重定向的方法等同于 return "redirect:/hello?param=value" ,
    // RedirectView#renderMergedOutputModel 方法,会修改 targetUrl 为 /hello?id1=123&name1=zhangsan1
    // 注意这种方法直接将传递的参数暴露在链接地址上,非常的不安全
    redirectAttribute.addAttribute("id1", "123");
    redirectAttribute.addAttribute("name1", "zhangsan1");
    return "redirect:/hello";
}

// curl -v -X GET "http://localhost:8080/param19_2"
// RedirectAttributesMethodArgumentResolver
@GetMapping("/param19_2")
public String param19_2(RedirectAttributes redirectAttribute) {
    // redirectAttribute 是 ModelAndViewContainer 的 redirectModel
    // 这种方法是隐藏了参数,链接地址上不直接暴露,但是能且只能在重定向的 “页面” 获取 param 参数值。
    // 其原理就是将设置的属性放到 session 中,session 中的属性在跳到页面后马上销毁
    // RedirectView#renderMergedOutputModel 方法,会把 FlashAttribute 放入 session 中
    // @ModelAttribute 注解绑定参数后
    redirectAttribute.addFlashAttribute("id1", "123");
    redirectAttribute.addFlashAttribute("name1", "zhangsan1");
    return "redirect:/hello";
}

// ServletModelAttributeMethodProcessor
@GetMapping("/hello")
public String hello(@ModelAttribute("id1") String id1) {
    return "hello";
}
zxm@zxm-pc:~$ curl -v -X GET "http://localhost:8080/param19"
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /param19 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 
< Location: http://localhost:8080/hello?id1=123&name1=zhangsan1
< Content-Language: zh-CN
< Content-Length: 0
< Date: Wed, 18 Sep 2024 02:31:12 GMT
< 
* Connection #0 to host localhost left intact
zxm@zxm-pc:~$ curl -v -X GET "http://localhost:8080/param19_2"
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /param19_2 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 
< Set-Cookie: JSESSIONID=BB0011AED9593231F8D9768D5ECFBCFB; Path=/; HttpOnly
< Location: http://localhost:8080/hello;jsessionid=BB0011AED9593231F8D9768D5ECFBCFB
< Content-Language: zh-CN
< Content-Length: 0
< Date: Wed, 18 Sep 2024 02:33:40 GMT
< 
* Connection #0 to host localhost left intact

20.ModelMethodProcessor 处理 Model

// curl -v -X GET "http://localhost:8080/param21"
// ModelMethodProcessor
@GetMapping("/param20")
public String param20(Model model) {
    return "param20";
}

21.MapMethodProcessor 处理 Map

// curl -v -X GET "http://localhost:8080/param21"
// MapMethodProcessor
@GetMapping("/param21")
public String param21(Map<String, String> map) {
    // 实际传入的是 ModelMap,相当于 Model
    return "param21";
}

22.ErrorsMethodArgumentResolver 处理 Errors

// curl "http://127.0.0.1:8080/param22?id1=123"
// 第一个参数使用 ModelAttributeMethodProcessor 解析, 第二个参数使用 ErrorsMethodArgumentResolver 解析
// ModelAttributeMethodProcessor 绑定数据;如果参数被 @Validated 标注,会校验数据。
// 如果校验失败,且下个参数不是 Errors,则抛出 BindException;如果校验失败,且下个参数是 Errors,把校验信息放入 Model 中
// ErrorsMethodArgumentResolver 用于从 Model 中取出 org.springframework.validation.BindingResult.pet 赋值给参数
@GetMapping("/param22")
public String param22(@Valid Pet pet, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        Map<String, String> errors = new HashMap<>();
        bindingResult.getFieldErrors().forEach(error ->
                errors.put(error.getField(), error.getDefaultMessage()));
        System.out.println(errors);
    }
    return "param22";
}

23.SessionStatusMethodArgumentResolver 处理 SessionStatus

// curl "http://127.0.0.1:8080/param23"
// SessionStatusMethodArgumentResolver
@GetMapping("/param23")
public String param23(SessionStatus sessionStatus) {
    return "param23";
}

24.UriComponentsBuilderMethodArgumentResolver 处理 UriComponentsBuilder

// curl "http://127.0.0.1:8080/param24"
// UriComponentsBuilderMethodArgumentResolver
@GetMapping("/param24")
public String param24(ServletUriComponentsBuilder uriComponentsBuilder) {
    return "param24";
}

25.PrincipalMethodArgumentResolver 处理 Principal

// curl "http://127.0.0.1:8080/param25"
// PrincipalMethodArgumentResolver
@GetMapping("/param25")
public String param25(Principal principal) {
    return "param25";
}

26.RequestParamMethodArgumentResolver(beanFactory, true) 处理 @RequestParam || isSimpleProperty(prameterType)

// curl -X GET "http://localhost:8080/param26?id1=123&name1=zhangsan"
// RequestParamMethodArgumentResolver(beanFactory, true)
@GetMapping("/param26")
public String param26(String id1) {
    return "param26";
}

27.ServletModelAttributeMethodProcessor(true) 处理 @ModelAttribute || !isSimpleProperty(prameterType)

// curl -X GET "http://localhost:8080/param27?id1=123&name1=zhangsan"
// ServletModelAttributeMethodProcessor(true)
@GetMapping("/param27")
public String param27(Pet pet) {
    return "param27";
}

1.ModelAndViewMethodReturnValueHandler 处理 ModelAndView

// curl -X GET "http://localhost:8080/r1"
// ModelAndViewMethodReturnValueHandler
@GetMapping("/r1")
public ModelAndView r1() {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("hello");

    Map<String, Object> model = modelAndView.getModel();
    model.put("name1", "zhangsan");

    return modelAndView;
}

2.ModelMethodProcessor 处理 Model

// curl -X GET "http://localhost:8080/r2"
// 返回的视图名,是请求 url,即 r2
// ModelMethodProcessor
@GetMapping("/r2")
public Model r2() {
    Model model = new BindingAwareModelMap();
    model.addAttribute("name1", "zhangsan");
    return model;
}

3.ViewMethodReturnValueHandler 处理 View

// curl -X GET "http://localhost:8080/r3"
// ViewMethodReturnValueHandler
@GetMapping("/r3")
public View r3() {
    return new RedirectView("https://www.baidu.com");
}

4.ResponseBodyEmitterReturnValueHandler 处理 ResponseEntity

// curl -X GET "http://localhost:8080/r4"
// ResponseBodyEmitterReturnValueHandler
@GetMapping("/r4")
public ResponseBodyEmitter r4() {
    ResponseBodyEmitter emitter = new ResponseBodyEmitter();
    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(5);
            emitter.send("this is r4");
            emitter.complete();
        } catch (Exception e) {
            emitter.completeWithError(e);
        }
    }).start();
    // ResponseBodyEmitterReturnValueHandler 会把 ResponseBodyEmitter 转化为 DeferredResult 处理
    return emitter;
}

S.StreamingResponseBodyReturnValueHandler 处理 StreamingResponseBody

// curl -X GET "http://localhost:8080/r5"
// StreamingResponseBodyReturnValueHandler
@GetMapping("/r5")
public StreamingResponseBody r5() {
    StreamingResponseBody stream = outputStream -> {
        try {
            for (int i = 0; i < 10; i++) {
                TimeUnit.SECONDS.sleep(1);
                String data = "Data chunk " + i + "\n";
                outputStream.write(data.getBytes());
                outputStream.flush();
            }
        } catch (Exception e) {
            outputStream.write(e.getMessage().getBytes());
            outputStream.flush();
        }
    };
    return stream;
}

6.HttpEntityMethodProcessor 处理 HttpEntity

// curl -X GET "http://localhost:8080/r6"
// HttpEntityMethodProcessor
@GetMapping("/r6")
public ResponseEntity<String> r6() {
    // HttpEntityMethodProcessor 会设置 mavContainer.setRequestHandled(true); 
    // 则处理器不会查找 view,不会渲染 view
    return ResponseEntity.ok().body("this is body");
}

7.HttpHeadersReturnValueHandler 处理 HttpHeaders

// curl -X GET "http://localhost:8080/r7"
// HttpHeadersReturnValueHandler
@GetMapping("/r7")
public HttpHeaders r7() {
    HttpHeaders headers = new HttpHeaders();
    headers.put("header1", Arrays.asList("11", "12"));
    headers.put("header2", Arrays.asList("21", "22"));
    // HttpHeadersReturnValueHandler 会设置 mavContainer.setRequestHandled(true);
    // 则处理器不会查找 view,不会渲染 view
    return headers;
}

8.CallableMethodReturnValueHandler 处理 Callable

// curl -X GET "http://localhost:8080/r8"
// CallableMethodReturnValueHandler
// 会使用 WebAsyncManager#DEFAULT_TASK_EXECUTOR 是 SimpleAsyncTaskExecutor 执行 Callable
@GetMapping("/r8")
public Callable<ResponseEntity<String>> r8() {
    Callable<ResponseEntity<String>> callable = () -> {
        TimeUnit.SECONDS.sleep(5);
        return ResponseEntity.ok().body("this is r8");
    };
    // 因 CallableMethodReturnValueHandler 会调用
    // WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer);
    // 处理器不会查找 view,不会渲染 view
    return callable;
}

9.DeferredResultMethodReturnValueHandler 处理 DeferredResult

// curl -X GET "http://localhost:8080/r9_2"
// DeferredResultMethodReturnValueHandler
// DeferredResult ListenableFuture CompletionStage
@GetMapping("/r9_1")
public DeferredResult<ResponseEntity<String>> r9_1() {
    DeferredResult<ResponseEntity<String>> deferredResult = new DeferredResult<>();
    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(5);
            // 执行 deferredResult.setResult(T result),会调用 resultHandlerToUse.handleResult(Object result);
            // deferredResult 的 resultHandlerToUse 赋值在 WebAsyncManager#startDeferredResultProcessing 中,
            // 会执行 asyncWebRequest.dispatch();
            deferredResult.setResult(ResponseEntity.ok().body("Task completed successfully"));
        } catch (InterruptedException e) {
            deferredResult.setErrorResult(e);
        }
    }).start();

    // 因 DeferredResultMethodReturnValueHandler 会调用
    // WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer);
    // 处理器不会查找 view,不会渲染 view
    return deferredResult;
}

// curl -X GET "http://localhost:8080/r9_2"
// DeferredResultMethodReturnValueHandler
// DeferredResult ListenableFuture CompletionStage
@GetMapping("/r9_2")
public ListenableFuture<ResponseEntity<String>> r9_2() {
    CompletableFuture<ResponseEntity<String>> completableFuture = new CompletableFuture<>();
    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(5);
            // 执行 completableFuture.complete(value) 会回调 completableFuture.whenComplete(action) 指定的函数
            // 定义在 CompletableToListenableFutureAdapter#CompletableToListenableFutureAdapter(completableFuture)
            // 然后调用到 DeferredResultMethodReturnValueHandler#adaptListenableFuture(future)
            // 后执行的是 DeferredResult.setResult(result) 逻辑
            // 神奇的 Adapter 模式
            completableFuture.complete(ResponseEntity.ok().body("this is ok"));
        } catch (InterruptedException e) {
            completableFuture.completeExceptionally(e);
        }
    }).start();
    // 因 DeferredResultMethodReturnValueHandler 会调用
    // WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer);
    // 处理器不会查找 view,不会渲染 view
    return new CompletableToListenableFutureAdapter<>(completableFuture);
}

10.AsyncTaskMethodReturnValueHandler 处理 WebAsyncTask

// curl -X GET "http://localhost:8080/r10"
// AsyncTaskMethodReturnValueHandler
@GetMapping("/r10")
public WebAsyncTask<ResponseEntity<String>> r10() {
    // 因 AsyncTaskMethodReturnValueHandler 会调用
    // WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(webAsyncTask, mavContainer);
    // 处理器不会查找 view,不会渲染 view
    return new WebAsyncTask<>(() -> ResponseEntity.ok().body("this is ok"));
}

11.ServletModelAttributeMethodProcessor 处理 @ModelAttribute

// curl -X GET "http://localhost:8080/r11"
// ServletModelAttributeMethodProcessor
// ServletModelAttributeMethodProcessor 处理把 @ModelAttribute 的 name 为 key, 返回值为 value, 放入 mavContainer 中
// 请求的 url 作为 view name
@GetMapping("/r11")
@ModelAttribute(name = "attribute11")
public String r11() {
    return "result r11";
}

12.RequestResponseBodyMethodProcessor 处理 @RequestBody

// curl -X GET "http://localhost:8080/r12"
// RequestResponseBodyMethodProcessor
// 因 RequestResponseBodyMethodProcessor 会调用
// mavContainer.setRequestHandled(true);
// 处理器不会查找 view,不会渲染 view
@ResponseBody
@GetMapping("/r12")
public String r12() {
    return "result r12";
}

13.ViewNameMethodReturnValueHandler 处理 void,String

// curl -X GET "http://localhost:8080/r13_1"
// ViewNameMethodReturnValueHandler
// 把返回值处理为 view name
@GetMapping("/r13_1")
public String r13_1_m() {
    return "view_r13";
}

// curl -X GET "http://localhost:8080/r13_2"
// ViewNameMethodReturnValueHandler
// 把请求 url 处理为 view name
@GetMapping("/r13_2")
public void r13_2_m() {
    System.out.println("- - - r13_2 - - -");
}

14.MapMethodProcessor 处理 Map

// curl -X GET "http://localhost:8080/r14"
// MapMethodProcessor
// MapMethodProcessor 把返回的 map,加入 mavContainer 中
// 请求的 url 作为 view name
@GetMapping("/r14")
public Map<String, String> r14_m() {
    Map<String, String> map = new HashMap<>();
    map.put("id1", "123");
    map.put("name1", "zhangsan");
    return map;
}

15.ServletModelAttributeMethodProcessor(true) 处理 @ModelAttribute 或 !isSimpleProperty(prameterType)

// curl -X GET "http://localhost:8080/15"
// ServletModelAttributeMethodProcessor(true)
// ServletModelAttributeMethodProcessor 处理把返回值的类型名小写为 key, 返回值为 value, 放入 mavContainer 中
// 请求的 url 作为 view name
@GetMapping("/15")
public Pet r15() {
    return new Pet(1, "pet");
}

在处理返回值时,设置 mavContainer.setRequestHandled(true); 或 WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer); 则处理器不会查找 view,不会渲染 view

三、原理

1.加载 WebMvcAutoConfiguration

spring-boot-autoconfigure-2.7.2.jar 下有文件
org.springframework.boot.autoconfigure.AutoConfiguration.imports,内有:

根据 META-INF 下文件,会加载 AopAutoConfiguration

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

2.注册 RequestMappingHandlerAdapter

EnableWebMvcConfiguration 继承 DelegatingWebMvcConfiguration,DelegatingWebMvcConfiguration 继承 WebMvcConfigurationSupport

注册 EnableWebMvcConfiguration 的时候,会调用 setConfigurers 方法,
把 WebMvcConfigurer 的 bean(此时会找到 WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter 和 自定义的 PetWebMvcConfigurer),赋值到 configurers 中

// WebMvcAutoConfiguration.java
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
        ValidationAutoConfiguration.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebMvcAutoConfiguration {
    ...
    @Configuration(proxyBeanMethods = false)
    @EnableConfigurationProperties(WebProperties.class)
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
        ...
        @Bean
        @Override
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
                @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
                @Qualifier("mvcConversionService") FormattingConversionService conversionService,
                @Qualifier("mvcValidator") Validator validator) {
            RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager,
                    conversionService, validator);
            adapter.setIgnoreDefaultModelOnRedirect(
                    this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
            return adapter;
        }   
        ...
    }   
    ...
}
// DelegatingWebMvcConfiguration.java
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    ...
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
    ...
}
// WebMvcConfigurationSupport.java
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
    ...
    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
            @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
            @Qualifier("mvcConversionService") FormattingConversionService conversionService,
            @Qualifier("mvcValidator") Validator validator) {

        RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
        adapter.setContentNegotiationManager(contentNegotiationManager);
        adapter.setMessageConverters(getMessageConverters());
        adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
        adapter.setCustomArgumentResolvers(getArgumentResolvers());
        adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

        if (jackson2Present) {
            adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
            adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
        }

        AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();
        if (configurer.getTaskExecutor() != null) {
            adapter.setTaskExecutor(configurer.getTaskExecutor());
        }
        if (configurer.getTimeout() != null) {
            adapter.setAsyncRequestTimeout(configurer.getTimeout());
        }
        adapter.setCallableInterceptors(configurer.getCallableInterceptors());
        adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

        return adapter;
    }

    protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() {
        if (this.argumentResolvers == null) {
            this.argumentResolvers = new ArrayList<>();
            addArgumentResolvers(this.argumentResolvers);
        }
        return this.argumentResolvers;
    }
    ...
}

RequestMappingHandlerAdapter 的 afterPropertiesSet 会赋值默认的 argumentResolvers、initBinderArgumentResolvers、returnValueHandlers

// RequestMappingHandlerAdapter.java
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    ...
    @Override
    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        initControllerAdviceCache();

        if (this.argumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        if (this.initBinderArgumentResolvers == null) {
            List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
            this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
        }
        if (this.returnValueHandlers == null) {
            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
        }
    }

    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ServletModelAttributeMethodProcessor(false));
        resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new RequestHeaderMapMethodArgumentResolver());
        resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new SessionAttributeMethodArgumentResolver());
        resolvers.add(new RequestAttributeMethodArgumentResolver());

        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());
        resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
        resolvers.add(new RedirectAttributesMethodArgumentResolver());
        resolvers.add(new ModelMethodProcessor());
        resolvers.add(new MapMethodProcessor());
        resolvers.add(new ErrorsMethodArgumentResolver());
        resolvers.add(new SessionStatusMethodArgumentResolver());
        resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
        if (KotlinDetector.isKotlinPresent()) {
            resolvers.add(new ContinuationHandlerMethodArgumentResolver());
        }

        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }

        // Catch-all
        resolvers.add(new PrincipalMethodArgumentResolver());
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
        resolvers.add(new ServletModelAttributeMethodProcessor(true));

        return resolvers;
    }

    private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

        // Annotation-based argument resolution
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
        resolvers.add(new RequestParamMapMethodArgumentResolver());
        resolvers.add(new PathVariableMethodArgumentResolver());
        resolvers.add(new PathVariableMapMethodArgumentResolver());
        resolvers.add(new MatrixVariableMethodArgumentResolver());
        resolvers.add(new MatrixVariableMapMethodArgumentResolver());
        resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
        resolvers.add(new SessionAttributeMethodArgumentResolver());
        resolvers.add(new RequestAttributeMethodArgumentResolver());

        // Type-based argument resolution
        resolvers.add(new ServletRequestMethodArgumentResolver());
        resolvers.add(new ServletResponseMethodArgumentResolver());

        // Custom arguments
        if (getCustomArgumentResolvers() != null) {
            resolvers.addAll(getCustomArgumentResolvers());
        }

        // Catch-all
        resolvers.add(new PrincipalMethodArgumentResolver());
        resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

        return resolvers;
    }

    private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
        List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

        // Single-purpose return value types
        handlers.add(new ModelAndViewMethodReturnValueHandler());
        handlers.add(new ModelMethodProcessor());
        handlers.add(new ViewMethodReturnValueHandler());
        handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
                this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
        handlers.add(new StreamingResponseBodyReturnValueHandler());
        handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
                this.contentNegotiationManager, this.requestResponseBodyAdvice));
        handlers.add(new HttpHeadersReturnValueHandler());
        handlers.add(new CallableMethodReturnValueHandler());
        handlers.add(new DeferredResultMethodReturnValueHandler());
        handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

        // Annotation-based return value types
        handlers.add(new ServletModelAttributeMethodProcessor(false));
        handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
                this.contentNegotiationManager, this.requestResponseBodyAdvice));

        // Multi-purpose return value types
        handlers.add(new ViewNameMethodReturnValueHandler());
        handlers.add(new MapMethodProcessor());

        // Custom return value types
        if (getCustomReturnValueHandlers() != null) {
            handlers.addAll(getCustomReturnValueHandlers());
        }

        // Catch-all
        if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
            handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
        }
        else {
            handlers.add(new ServletModelAttributeMethodProcessor(true));
        }

        return handlers;
    }
    ...
}

3.执行

3.1 找到 RequestMappingHandlerAdapter

DispatcherServlet 的 doDispatch 会处理请求,找到 RequestMappingHandlerAdapter

此 HandlerAdapter 值是 RequestMappingHandlerAdapter

// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ...
    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            ...
            mappedHandler = getHandler(processedRequest);
            ...
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            ...
            // Actually invoke the handler.
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            ...
        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            ...
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        ...
    }
    catch (Throwable err) {
        ...
    }
    finally {
        ...
    }
}

3.2 RequestMappingHandlerAdapter 处理请求

// RequestMappingHandlerAdapter.java
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {

    return handleInternal(request, response, (HandlerMethod) handler);
}

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ...
    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
        ...
    }
    else {
        // No synchronization on session demanded at all...
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }
    ...
    return mav;
}

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
        ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }
        invocableMethod.setDataBinderFactory(binderFactory);
        invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

        ModelAndViewContainer mavContainer = new ModelAndViewContainer();
        mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        modelFactory.initModel(webRequest, mavContainer, invocableMethod);
        mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

        AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
        asyncWebRequest.setTimeout(this.asyncRequestTimeout);
        ...
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}

3.3 解析参数和处理返回值

执行 ServletInvocableHandlerMethod,resolvers.resolveArgument 解析参数,returnValueHandlers.handleReturnValue 处理返回值

// ServletInvocableHandlerMethod.java
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);
    ...
    try {
        this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }
    catch (Exception ex) {
        ...
        throw ex;
    }
}

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    ...
    return doInvoke(args);
}

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }

    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs);
        if (args[i] != null) {
            continue;
        }
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        }
        catch (Exception ex) {
            ...
            throw ex;
        }
    }
    return args;
}