文章
问答
冒泡
Spring MVC处理流程分析

思考

如果现在我们有这么一个 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 那么

  1. 响应是Json数据,还是跳转页面?

  2. 如果是跳转页面,那么下面的页面能正确获取数据: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>

更多的问题

  1. 请求为什么能找到 SimpleController 中的方法 method?

  2. 请求的age是字符串类型,为什么能转换成Integer的类型?

  3. 为什么User对象加了注解 @Valid 就可以对对象进行验证?

  4. 参数age加上注解(@Valid @NotBlank Integer age)可以验证吗?

  5. 为什么 BindingResult 参数需要紧跟在 @Valid 参数后面?没有参数 BindingResult 参数,方法验证通过会抛出异常,有这个参数就不抛出异常?

WebMvcAutoConfiguration自动配置

start.png

请求响应处理流程分析

一般过程

f1.png

具体过程

f2.png

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。实际上调用的是 RequestMappingHandlerAdapterinvokeHandlerMethod 方法。
RequestMappingHandlerAdapterinvokeHandlerMethod 方法有2个核心方法:

  • invocableMethod.invokeAndHandle(webRequest, mavContainer)

处理参数并得到方法的返回结果。通过名字可以知道 invokeAndHandle 也做了两件事情:invokehandle

	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);
		}
	}

回到思考

回到前面的思考,我们可以分析。
f3.png


关于作者

Rick
获得点赞
文章被阅读