【spring】 spring mvc 处理 http 请求的过程

分析 spring mvc 如何处理 http 请求

前言

spring mvc 是通过 servlet 技术实现的一个模型-视图-控制器的 web 框架,http 处理的过程大体如下:

spring mvc 架构图

front controller 接受到请求,然后传给 controller,controller 经过计算后返回一个 model,然后 front controller 将 model 传给 view template,之后将 view tmeplate 渲染后的结果返回。

实现

http 请求处理过程

spring mvc 通过 servlet 实现,http 请求的处理过程主要在 DispatcherServlet 的 doDispatch 方法实现。分两个步骤,第一步:通过 handler 和 HandlerAdapter 获取得到一个 ModelAndView 对象;第二步:根据得到的 ModelAndView 渲染返回结果。

DispatcherServlet 继承 FrameworkServlet,而 FrameworkServlet 最终继承 javax.servlet.http.HttpServlet。HttpServlet 提供 doGet, doPost 等方法让子类覆写以处理相关的 http 请求。在 FrameworkServlet 中,所有这些方法都是调用 processRequest 来处理。processRequest 的主要做一些 ThreadLocal 的初始化与清理,并且发布事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

...

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;

LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);

RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

// 同异步请求有关
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

// 初始化 LocaleContextHolder 和 RequestContextHolder,两者都是 ThreadLocal 对象的封装
// 所以在开始时需要 init,在结束时需要 clean
initContextHolders(request, localeContext, requestAttributes);

try {
// 处理请求,由子类实现
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
// 清理 LocaleContextHolder 和 RequestContextHolder
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
// 如果日志级别是 debug,则打印日志
logResult(request, response, failureCause, asyncManager);
// 发布 ServletRequestHandledEvent 事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
}

doService 的主要作用是对 http request 对象的属性做一些处理,然后调用核心的 doDispatch 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class DispatcherServlet extends FrameworkServlet {
...

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 如果日志级别是 debug,则打印日志
logRequest(request);

// 为 request 对象保存一份原始的属性值
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}

// 设置一些框架的属性
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}

try {
// 关键方法
doDispatch(request, response);
}
finally {
// 如果需要,就还原之前保存的属性
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
}

doDispatch 方法是 spring mvc 的核心,它实现了 http 请求的处理过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class DispatcherServlet extends FrameworkServlet {

// 用于解析 multipart 请求,由 initMultipartResolver 方法初始化
// 默认用 StandardServletMultipartResolver 实现
private MultipartResolver multipartResolver;

// 由 initHandlerMappings 方法初始化
// HandlerMapping 会根据 request 返回一个 HandlerExecutionChain
// HandlerExecutionChain 由 handler 和多个 HandlerInterceptor 组成,
// handler 处理请求,HandlerInterceptor 负责在请求的处理前后实现一些功能
private List<HandlerMapping> handlerMappings;

// 由 initHandlerAdapters 方法初始化
private List<HandlerAdapter> handlerAdapters;

...

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
// 检查是否 multipark 请求, 如果就由 multipartResolver 解释出一个新的 request 对象,否则返回传入的 request
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// getHandler 会遍历 handlerMappings,依次调用 HandlerMapping 的 getHandler 方法
// 返回第一个非 null 的 HandlerExecutionChain 对象
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// 从 handlerAdapters 里找到支持这个 handler 的 HandlerAdapter
// 这是一个扩展点
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// 如果 handler 支持的话,就处理 last-modified 这个 http 头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

// 执行 HandlerExecutionChain 里的 HandlerInterceptor 的 preHandle 方法
// preHandle 返回 false 的话,会中断整个 http 处理过程
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 调用 HandlerAdapter 的 handler 返回, 返回一个 ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 如果是异步的话,现在可以返回了
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

// 给 view 一个默认名称(如果没有的话)
applyDefaultViewName(processedRequest, mv);

// 执行 HandlerExecutionChain 里的 HandlerInterceptor 的 postHandle 方法
// 这个是从尾到头执行
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}

// 处理 dispatch 的结果
// 渲染错误页面或渲染 view
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 执行 HandlerExecutionChain 里的 HandlerInterceptor 的 afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
}

doDispatch 通过 HandlerExecutionChain 和 HandlerAdapter 得到 ModelAndView 对象 mv, 再调用 processDispatchResult 进行渲染。

计算 ModelAndView 对象详解

HandlerExecutionChain 和 HandlerAdapter 是计算 ModelAndView 的关键,下面详细描述

HandlerExecutionChain

HandlerExecutionChain 是对 handler 和 HandlerInterceptor 的包装。handler 可以是任意类型的对象,而 HandlerInterceptor 可以在 handler 处理的前后插入一些其他的处理。一个 HandlerExecutionChain 实质上就对应着一个 handler 对象

在 getHandler 方法中,通过循环调用 handlerMappings 列表中的每个 HandlerMapping 的 getHandler 方法,得到一个 HandlerExecutionChain 对象。getHandler 接受 request 作为参数,说明HandlerMapping 通过分析 request 对象中的参数(通常就是 URL)决定返回具体的 HandlerExecutionChain。

1
2
3
4
5
6
7
8
9
10
11
12

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

HandlerMapping 有两大类:

  • UrlHandlerMapping
    返回任意类型对象作为 handler

  • HandlerMethodMapping
    返回的是 HandlerMethod 类型对象作为 handler。HandlerMethod 是一个类,包含一个 bean,java.lang.reflect.Method 以及调用 Method 的参数。

两类 HandlerMapping 的返回值都用 HandlerExecutionChain 封装。

handlerMappings 列表通过 initHandlerMappings 初始化,从 bean factory 中获取所有类型是 HandlerMapping 的 bean。默认的 HandlerMapping 有

  • SimpleUrlHandlerMapping
    简单的 URL 到 handler 的映射,URL 可以用通配符匹配

  • RequestMappingHandlerMapping
    通过 @RequestMapping 注解,注册的 URL 和 HandlerMethod(保存被注解的方法) 的映射

  • BeanNameUrlHandlerMapping
    如果 bean 的名称或别名包含 “/“,就建立 bean 名当成 URL,bean 当成 handler 的映射

  • WebMvcEndpointHandlerMapping(引入 actuator 才有)
    actuator 的 HandlerMapping,建立 URL 和 endpoint 的映射

HandlerAdapter

从 HandlerMapping 得到 handler 后,需要得到对应的 HandlerAdapter。DispatcherServlet 通过 HandlerAdapter 来调用 handler,这意味着一个 handler 必须有对应的 HandlerAdapter,否则无法处理这个执行 handler。

HandlerAdapter 接口的实现如下:

1
2
3
4
5
6
7
8
9
10
11
public interface HandlerAdapter {

// HandlerAdapter 是否支持这个 handler
boolean supports(Object handler);

// 调用 handler 处理请求
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

// 用来处理 last-modified 头
long getLastModified(HttpServletRequest request, Object handler);
}

handlerAdapters 通过 initHandlerAdapters 方法初始化,同 initHandlerMappings 逻辑一样,都是在 bean factory 找到所有的 HandlerAdapter。

默认的 HandlerAdapter 有

  • RequestMappingHandlerAdapter
    支持 HandlerMethod,handle 方法返回 HandlerMethod 对目标方法的调用结果。有 @RequestMapping 注解修饰的方法就是用这个 adapter 处理。

  • HttpRequestHandlerAdapter
    支持实现了 HttpRequestHandler 接口的 handler,handle 方法直接调用 HttpRequestHandler 的 handleRequest 方法处理请求,返回 null

  • SimpleControllerHandlerAdapter
    支持实现了 org.springframework.web.servlet.mvc.Controller 接口的 handler,handle 返回 Controller 的 handleRequest 方法的结果

@RequestMapping 注解处理过程

通过 @RequestMapping 注解 controller 的方法,是使用 spring mvc 的一种最常见的方式。用户通过 @RequestMapping 注解定义了从 URL 到 controller 方法间的一个映射,由 RequestMappingHandlerMapping 保存着所有这样的用户定义的映射。在处理 http 请求时,RequestMappingHandlerMapping 返回一个具体的 HandlerMethod 作为 handler,而 RequestMappingHandlerAdapter 将负责适配这种类型的 handler。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {

// 其实这是基类 AbstractHandlerMethodAdapter 的方法
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// hanlder 的类型是 HandlerMethod
return handleInternal(request, response, (HandlerMethod) handler);
}

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// 在有 session 的情况下是否要同步执行,默认是 false
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 调用目标方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}

// 处理 Cache-Control 头
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}

return mav;
}

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

// 通过封装,更容易操作 request 和 response
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// WebDataBinderFactory 生产 WebDataBinder, WebDataBinder 将 request 里的参数绑定到 java bean
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// ModelFactory 用于初始化和更新 Model
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

// ServletInvocableHandlerMethod 是 HandlerMethod 的子类
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
// parameterNameDiscoverer 用于分析方法原型,找到参数名称
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

// ModelAndViewContainer 用来保存执行的结果
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

// servlet 异步调用相关
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}

/*
* 调用目标方法,结果保存到 mavContainer
* 使用 ParameterNameDiscoverer 和 HandlerMethodArgumentResolver 从 request 提取调用目标方法的参数
* 调用目标方法
* 将结果通过 HandlerMethodReturnValueHandler 保存到 mavContainer
*/
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}

// 构造一个 ModelAndView 对象返回,如果 mavContainer.isRequestHandled() 为 true,则返回 null,表示不需要渲染结果
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
}
目标方法的调用

方法调用的过程如下:

1
2
3
4
5
6
7
8
9
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

// 从 request 中提取参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

// 反射调用方法
return doInvoke(args);
}

第一步,从 request 中提取参数

  1. 获取 MethodParameter 对象数组。MethodParameter 封装方法的参数信息,在方法注册到 HandlerMapping 时通过 GenericTypeResolver.resolveParameterType 解析出来

  2. 遍历 MethodParameter 对象数组,找到合适的 HandlerMethodArgumentResolver,然后用这个 HandlerMethodArgumentResolver 从 request 中逐个提取参数的值。

  3. 调用每个找到的 HandlerMethodArgumentResolver 的 resolveArgument 方法解析出参数

注:默认情况下有如下 HandlerMethodArgumentResolver

  • RequestParamMethodArgumentResolver:解析有 @RequestParam 注解修饰,类型不是 Map 的参数,从 http 请求参数中得到调用目标方法的参数
  • RequestParamMapMethodArgumentResolver:同上,解析类型是 Map 的参数
  • PathVariableMethodArgumentResolver:解析有 @PathVariable 注解修饰,类型不是 Map 的参数,从 url 中得到调用目标方法的参数
  • PathVariableMapMethodArgumentResolver:同上,解析类型是 Map 的参数
  • MatrixVariableMethodArgumentResolver:解析有 @MatrixVariable 注解修饰,类型不是 Map 的参数
  • MatrixVariableMapMethodArgumentResolver:同上,解析类型是 Map 的参数
  • ServletModelAttributeMethodProcessor:解析有 @ModelAttribute 注解修饰的参数
  • RequestResponseBodyMethodProcessor:解析有 @RequestBody 注解修饰的参数,从请求的 body 得到参数
  • RequestPartMethodArgumentResolver:解析有 @RequestPart 注解修饰的参数
  • RequestHeaderMethodArgumentResolver:解析有 @RequestHeader 注解修饰的,类型不是 Map 的参数
  • RequestHeaderMapMethodArgumentResolver:同上,解析类型是 Map 的参数
  • ServletCookieValueMethodArgumentResolver:解析有 @CookieValue 注解修饰的参数
  • ExpressionValueMethodArgumentResolver:解析有 @Value 注解修饰的参数
  • SessionAttributeMethodArgumentResolver:解析有 @SessionAttribute 注解修饰的参数
  • RequestAttributeMethodArgumentResolver:解析有 @RequestAttribute 注解修饰的参数,从 request 的 attribute 中得到调用参数
  • ServletRequestMethodArgumentResolver:解析 ServletRequest 等类型参数
  • ServletResponseMethodArgumentResolver:解析 ServletResponse 等类型的参数
  • HttpEntityMethodProcessor:解析 HttpEntity,RequestEntity 类型的参数
  • RedirectAttributesMethodArgumentResolver:解析 RedirectAttributes 类型参数
  • ModelMethodProcessor:解析 Model 类型参数
  • MapMethodProcessor:解析 Map 类型参数
  • ErrorsMethodArgumentResolver:解析 Errors 类型参数
  • SessionStatusMethodArgumentResolver:解析 SessionStatus 类型参数
  • UriComponentsBuilderMethodArgumentResolver:解析 UriComponentsBuilder 和 ServletUriComponentsBuilder 类型参数

第二步,反射调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());

try {
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
...
}
catch (InvocationTargetException ex) {
...
}
}

桥接方法是 JDK 1.5 引入泛型后,为了使 Java 的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。可以通过 Method.isBridge() 来判断一个方法是否是桥接方法。getBridgedMethod() 通过 isBridge 判断:如果是 true 就遍历所有方法找到对应的桥接方法;如果是 false 就返回原方法。

方法返回结果处理

调用目标方法所返回的结果,需要用 HandlerMethodReturnValueHandler 再进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {

// 上面提到的调用方法,returnValue 就是返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

// 设置 response status
setResponseStatus(webRequest);

// 处理返回值是 null 或 responseStatusReason 有值的情况
// setRequestHandled 用来表示 handler 是否完全处理了 request
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);

// 用 returnValueHandlers 处理结果
// 从 returnValueHandlers 中选择一个合适的 HandlerMethodReturnValueHandler,然后调用它的 handleReturnValue 方法
try {
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
throw ex;
}
}

注:默认情况下有如下 HandlerMethodReturnValueHandler

  • ModelAndViewMethodReturnValueHandler:处理返回类型是 ModelAndView 的情况
  • ModelMethodProcessor:处理返回类型是 Model 的情况
  • ViewMethodReturnValueHandler:处理返回类型是 View 的情况
  • ResponseBodyEmitterReturnValueHandler:处理返回类型是 ResponseBodyEmitterReturnValueHandler 或 ResponseEntity<ResponseBodyEmitterReturnValueHandler> 的情况
  • StreamingResponseBodyReturnValueHandler:处理返回类型是 StreamingResponseBody 或者 ResponseEntity<StreamingResponseBody> 的情况
  • HttpEntityMethodProcessor:处理返回类型是 HttpEntity 且不是 RequestEntity 的情况
  • HttpHeadersReturnValueHandler:处理返回类型是 HttpHeaders 的情况
  • CallableMethodReturnValueHandler:处理返回类型是 Callable 的情况
  • DeferredResultMethodReturnValueHandler:处理返回类型是 DeferredResult,ListenableFuture 和 CompletionStage 的情况
  • AsyncTaskMethodReturnValueHandler:处理返回类型是 WebAsyncTask 的情况
  • ModelAttributeMethodProcessor:处理返回类型是 ModelAttribute 或其它对象类型的情况
  • RequestResponseBodyMethodProcessor:处理方法有 RequestBody 注解的情况
  • ViewNameMethodReturnValueHandler:处理返回一个字符串的情况,这个字符串会被视为 view 的名字
  • MapMethodProcessor:处理返回一个 Map 的情况
@RequestBody 和 @RequestBody 注解的处理

RequestBody 和 RequestBody 是比较常见的注解,分别将 http 消息转换为对象和将对象转换为 http 消息,两者都是用 RequestResponseBodyMethodProcessor 进行处理。RequestResponseBodyMethodProcessor 同时实现了 HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler

处理 request 的过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

parameter = parameter.nestedIfOptional();

// 将 http 消息转换为对象
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);

if (binderFactory != null) {
// 验证参数
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}

// 适配 Optional 类型参数
return adaptArgumentIfNecessary(arg, parameter);
}

@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);

Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

// 获取请求中的 Content-Type 消息头
MediaType contentType;
boolean noContentType = false;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}

// 要调用的方法所在的类
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}

// 得到 http 方法(GET 还是 POST 等)
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;

EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);

// 找到合适的 HttpMessageConverter 从 inputMessage 中得到对象
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);

// canRead 返回 true 表示 HttpMessageConverter 可以处理
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {

// before body read
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);

// read
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));

// after body read
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
// 处理消息体为空的情况
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}

// 处理 null 的情况
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}

return body;
}

处理 response 的过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

// 设置为已处理,后续就不渲染了
mavContainer.setRequestHandled(true);

// inputMessage 就 ServletServerHttpRequest
// outputMessage 是 ServletServerHttpResponse
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

// 将对象转换为 http 消息
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

Object body;
Class<?> valueType;
Type targetType;

if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}

// 返回值是 Resource 类型的话,从资源读入
if (isResourceType(value, returnType)) {

// 设置 Accept-Ranges 头
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");

// HttpHeaders.RANGE 表示需要服务端返回指定范围的数据
if (value != null &&
inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {

Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}

// 选择适合的 MediaType 作为返回类型
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType != null && contentType.isConcrete()) {
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();

// 根据请求的 A
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
// 根据返回类型,得到
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);

if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
}

// 比较 acceptableTypes 和 producibleTypes,得到所有可能的
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}

// 如果没有,抛出异常
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
return;
}

// MediaType 排序
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);

// 取第一个适合的 MediaType
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
}

// 到这里 selectedMediaType 就是合适的返回类型

if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();

// 找到合适的 HttpMessageConverter 将 body 写到 outputMessage
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);

if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {

// write 之前可以做一些额外处理,例如统一返回格式
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);

if (body != null) {
Object theBody = body;

// 如果需要的话,加入 Content-Disposition 头
addContentDispositionHeader(inputMessage, outputMessage);

// 将对象写到 outputMessage
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
return;
}
}
}

// 没匹配的 MediaType 就抛出异常
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}

默认的 HttpMessageConverter 有

  • ByteArrayHttpMessageConverter:读写 byte 数组
  • StringHttpMessageConverter:读写 String 类型数据
  • ResourceHttpMessageConverter:读写 Resource 类型数据
  • ResourceRegionHttpMessageConverter:写 ResourceRegion 类型数据
  • SourceHttpMessageConverter:读写 javax.xml.transform.Source 类型数据
  • AllEncompassingFormHttpMessageConverter:读写 http from 数据(application/x-www-form-urlencoded,multipart/form-data)
  • MappingJackson2HttpMessageConverter:用 Jackson 读写 json 格式数据
  • Jaxb2RootElementHttpMessageConverter:用 JAXB 读写 xml 格式数据

用户也可以定义自己的 HttpMessageConverter 来实现对不同格式的 body 进行读写

静态资源处理过程

静态资源由 ResourceHttpRequestHandler 负责处理。ResourceHttpRequestHandler 实现了 HttpRequestHandler 接口,由 HttpRequestHandlerAdapter 进行适配处理,HttpRequestHandlerAdapter 调用 HttpRequestHandler 的 handleRequest 方法。

1
2
3
4
5
6
7
8
9
10
11
public class HttpRequestHandlerAdapter implements HandlerAdapter {

@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
}

ResourceHttpRequestHandler 将资源读出,然后返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

public class ResourceHttpRequestHandler extends WebContentGenerator
implements HttpRequestHandler, EmbeddedValueResolverAware, InitializingBean, CorsConfigurationSource {

@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

// 通过 ResourceResolver 从 request 解析出要返回的资源,如果没找到就返回 404
Resource resource = getResource(request);
if (resource == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}

// 如果是 OPTIONS 请求,就返回
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return;
}

// 只支持 GET 和 HEAD 请求
checkRequest(request);

// 检查资源是否有更新,如果没有就返回。
if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {
return;
}

// 处理可能的 Cache-Control http 头
prepareResponse(response);

// 根据资源文件后缀得到数据类型
MediaType mediaType = getMediaType(request, resource);

// 如果是 HEAD 请求,就返回
if (METHOD_HEAD.equals(request.getMethod())) {
setHeaders(response, resource, mediaType);
return;
}

// 从文件读入数据并写到 response
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
if (request.getHeader(HttpHeaders.RANGE) == null) {
// 没有 RANGE 头的情况,用 resourceHttpMessageConverter 写
Assert.state(this.resourceHttpMessageConverter != null, "Not initialized");

setHeaders(response, resource, mediaType);
this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
}
else {
// 有 RANGE 头的情况,用 resourceRegionHttpMessageConverter 写
Assert.state(this.resourceRegionHttpMessageConverter != null, "Not initialized");

response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
this.resourceRegionHttpMessageConverter.write(
HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage);
}
catch (IllegalArgumentException ex) {
response.setHeader("Content-Range", "bytes */" + resource.contentLength());
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
}
}

}

ModelAndView 渲染详解

通过 handler 计算出 ModelAndView 后,需要调用 processDispatchResult 对结果进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class DispatcherServlet extends FrameworkServlet {

...

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) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}

// 如果返回一个 view,渲染这个 view
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}

if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}

// 调用 HandlerInterceptor 的 afterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

处理有两种情况,要么是处理抛出异常的情况,要么是渲染结果

异常处理的过程如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class DispatcherServlet extends FrameworkServlet {

...

// 异常处理的过程
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {

request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

// 如果有 HandlerExceptionResolver, 用就 HandlerExceptionResolver 处理
// 初始化时,在 bean factory 找出所有 HandlerExceptionResolver 类型的 bean 加入到 handlerExceptionResolvers
// 用户可以定义自己的 HandlerExceptionResolver 进行统一的异常处理
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}

// HandlerExceptionResolver 返回一个 ModelAndView 对象
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}

if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}

// 如果 exMv 为空,则继续抛出原始异常

throw ex;
}
}

找到合适的 View 对象,然后渲染结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class DispatcherServlet extends FrameworkServlet {

...

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 计算及设置 response 的 locale
Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);

// 通过 ViewResolver 或 ModelAndView 获取 View 对象
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 通过 ViewResolver 解析一个 View
// 在初始化时,通过在 bean factory 搜索所有 ViewResolver 类型的 bean 到 viewResolvers
// 用户可以定义自己的 ViewResolver
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}

try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 渲染
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
throw ex;
}
}
}

spring boot 支持多种模板引擎,有不同的 ViewResolver 对应。例如有 ThymeleafViewResolver 用于处理 thymeleaf 模板, FreeMarkerViewResolver 用于处理 freemarker 模板。用户只需要把相应的 starter 加入到 pom.xml, spring boot 的 auto configuration 机制会将这些 ViewResolver 注册到 bean factory。