思考
如果现在我们有这么一个 SimpleController.java
@RestController
public class SimpleController {
@GetMapping("request")
@ModelAttribute("attr")
public Map<String, Object> method(Integer age, @Valid User user, BindingResult bindingResult) {
Map<String, Object> map = new HashMap<>();
map.put("age", age);
return map;
}
}
User.java
public class User {
private Integer age;
@NotBlank
private String name;
}
当我们发出请求:http://localhost:8080/request?name=Rick&age=23
那么,
- 响应是Json数据,还是跳转页面?
- 如果是跳转页面,那么下面的页面能正确获取数据:name => Rick, age => 23 吗?
<h1 th:text="'name => '+ ${#request.getAttribute('user').name}"></h1>
<h1 th:text="'age => ' + ${#request.getAttribute('attr').age}"></h1>
<h1 th:text="'age => ' + ${attr.age}"></h1>
<h1 th:text="'age => ' + ${user.age}"></h1>
我们可以继续问更多的问题
- 请求为什么能找到
SimpleController
中的方法method
? - 请求的age是字符串类型,为什么能转换成Integer的类型?
- 为什么User对象加了注解
@Valid
就可以对对象进行验证? - 参数age加上注解(@Valid @NotBlank Integer age)可以验证吗?
- 为什么
BindingResult
参数需要紧跟在 @Valid 参数后面?没有参数BindingResult
参数,方法验证通过会抛出异常,有这个参数就不抛出异常?
WebMvcAutoConfiguration自动配置
请求响应处理流程分析
一般过程
具体过程
1. HandlerExecutionChain
HandlerExecutionChain mappedHandler = getHandler(processedRequest);
HandlerExecutionChain
:依赖 handlerMapping
目的是找到需要执行的Controller的目标方法和拦截器Interceptor。默认加载5个handlerMapping
- 0 = {RequestMappingHandlerMapping@5687}
- 1 = {WelcomePageHandlerMapping@7500}
- 2 = {BeanNameUrlHandlerMapping@7501}
- 3 = {RouterFunctionMapping@7502}
- 4 = {SimpleUrlHandlerMapping@7503}
2. 获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
默认有4个适配器:
- 0 = {RequestMappingHandlerAdapter@8086}
- 1 = {HandlerFunctionAdapter@8087}
- 2 = {HttpRequestHandlerAdapter@8088}
- 3 = {SimpleControllerHandlerAdapter@8089}
RequestMappingHandlerAdapter
是我们常用的适配器,这个适配器聚合了请求阶段所使用的解析器/处理器。就像一个生产工厂一样,是一个非常重要的类。
比如:
- HandlerMethodArgumentResolver:「方法的参数转换」的解析器
- HandlerMethodReturnValueHandler:「方法返回值」处理器
- HttpMessageConverter: 消息转换
- RequestBodyAdvice:RequestBody请求值的处理
- ResponseBodyAdvice:ResponseBody返回值的处理
3. 处理拦截器PreHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
可以获取被拦截的方法,哪个controller的哪个方法。
4. HandlerAdapter#handle
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
适配开始处理,并获取ModelAndView。实际上调用的是 RequestMappingHandlerAdapter
的 invokeHandlerMethod
方法。
RequestMappingHandlerAdapter
的 invokeHandlerMethod
方法有2个核心方法:
- invocableMethod.invokeAndHandle(webRequest, mavContainer)
处理参数并得到方法的返回结果。通过名字可以知道 invokeAndHandle
也做了两件事情:invoke
和 handle
。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
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);
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
invokeForRequest
这个方法处理参数(依赖 HandlerMethodArgumentResolver
),并获取方法的返回值(获取参数后,反射)。mavContainer.setRequestHandled 设置请求是否被处理,后面视图渲染就不作处理。
「类型的转换」「对象的验证」都是在参数解析中完成的。「类型的转换」底层依赖 TypeConverterDelegate
默认的处理器有:
0 = {RequestParamMethodArgumentResolver@8604}
1 = {RequestParamMapMethodArgumentResolver@8605}
2 = {PathVariableMethodArgumentResolver@8606}
3 = {PathVariableMapMethodArgumentResolver@8607}
4 = {MatrixVariableMethodArgumentResolver@8608}
5 = {MatrixVariableMapMethodArgumentResolver@8609}
6 = {ServletModelAttributeMethodProcessor@8610}
7 = {RequestResponseBodyMethodProcessor@8611}
8 = {RequestPartMethodArgumentResolver@8612}
9 = {RequestHeaderMethodArgumentResolver@8613}
10 = {RequestHeaderMapMethodArgumentResolver@8614}
11 = {ServletCookieValueMethodArgumentResolver@8615}
12 = {ExpressionValueMethodArgumentResolver@8616}
13 = {SessionAttributeMethodArgumentResolver@8617}
14 = {RequestAttributeMethodArgumentResolver@8618}
15 = {ServletRequestMethodArgumentResolver@8619}
16 = {ServletResponseMethodArgumentResolver@8620}
17 = {HttpEntityMethodProcessor@8621}
18 = {RedirectAttributesMethodArgumentResolver@8622}
19 = {ModelMethodProcessor@8623}
20 = {MapMethodProcessor@8624}
21 = {ErrorsMethodArgumentResolver@8625}
22 = {SessionStatusMethodArgumentResolver@8626}
23 = {UriComponentsBuilderMethodArgumentResolver@8627}
24 = {PrincipalMethodArgumentResolver@8629}
25 = {RequestParamMethodArgumentResolver@8630}
26 = {ServletModelAttributeMethodProcessor@8631}
returnValueHandlers.handleReturnValue
对上一步的返回值做进一步处理。遍历所有的 HandlerMethodReturnValueHandler
找出合适的一个。然后对返回值做进一步处理,比如:把返回值放入Model中。默认有15个返回值处理器。
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}
- getModelAndView(mavContainer, modelFactory, webRequest)
获取模型和视图
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
// 处理过了,就不需要视图了
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
// 如果指定了具体的视图
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
5. 处理拦截器PostHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView)
可以对视图作出最后的处理
6. 视图渲染
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
// 如果有异常,那么准备异常视图
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// ExceptionHandlerExceptionResolver 会拿到 @ControllerAdvice的ExceptionHandler 去处理
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 如果有视图就渲染(@ResponseBody注解的requestHandled = true,所以视图是null,不需要渲染)
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// 处理拦截器afterCompletion方法,此时视图已经渲染完成。
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
回到思考
回到前面的思考,我们开始分析。