一、使用
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;
}