SpringMVC请求处理流程

Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中。 正式名称“Spring Web MVC”来自其源模块的名称(spring-webmvc),但它通常被称为“Spring MVC”。

与其他许多Web框架一样,Spring MVC围绕前端控制器模式进行设计,在该模式下,DispatcherServlet作为中央Servlet提供了用于请求处理的共享算法,而实际工作是由可配置的委托组件执行的。该模型非常灵活,并支持多种工作流程。

主体流程分析

DispatcherServlet 处理请求的核心逻辑入口为 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
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
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 {
// 检查请求是否为 Multipart 请求,若是则进行请求转换
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// 确定当前请求的处理程序
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// 确定当前请求处理程序适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

... ...

// 调用拦截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 实际调用处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

applyDefaultViewName(processedRequest, mv);
// 调用拦截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理请求结果,并调用拦截器的 triggerAfterCompletion 方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

... ...
}

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;

... ...

// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}

... ...

if (mappedHandler != null) {
// 调用拦截器的 afterCompletion 方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 确定请求的语言环境并将其应用于响应
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);

View view;
if (mv.isReference()) {
// 解析视图名称
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
... ...
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
... ...
}

// 委托给View对象进行渲染
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
throw ex;
}
}

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
// 使用已注册的视图解析器进行视图解析
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}

为子节约篇幅,对部分代码进行了删减,主要包括文件处理、异常处理、异步处理,这里只讨论普通的正常流程,整个流程可以分为如下几个步骤:

  1. 通过 HandlerMapping 获取当前请求的处理程序 HandlerExecutionChainHandlerExecutionChain 是一个 Facade,由一个实际处理类与一组 HandlerInterceptor 组成。实际处理类一般为 HandlerMethod ,也可以是 HttpRequestHandlerServlet 等;
  2. 获取支持实际处理类对应的适配器 HandlerAdapter,由于实际的处理类实现的方式有很多,这里使用适配器模式可以屏蔽掉实现类对调用逻辑的影响;
  3. 调用拦截器的 preHandle() 方法
  4. 通过 HandlerAdapter 完成实际的业务逻辑处理
  5. 调用拦截器的 postHandle() 方法
  6. 使用 ViewResolver 解析视图并进行渲染
  7. 调用拦截器的 afterCompletion() 方法

整个调用流程的时序图如下:

SpringMVC请求处理流程

常用扩展点

对于 SpringMVC,了解主体流程是远远不够的,一旦需要增加某些特性功能时,可能会很迷茫。比如:如何做全局的异常处理?如何对所有 Restful 请求进行日志记录?有可能你会说,我可以自定义一个切面进行拦截。那你是不是要新定义一套规范,让所有人按照既定规范来实现?还是说定义一个注解,每个接口自行标注?无论哪种实现都显得太复杂,不够优雅。实际上,如果你了解 SpringMVC 请求流程中预留的扩展点,这些问题都非常简单。

HandlerInterceptor

HandlerInterceptor基本上类似于Servlet过滤器,但与后者相比,它仅允许自定义预处理以及禁止执行处理程序本身和自定义后处理的选项。通常用作授权检测,具体的触发节点在上述主体流程中已明确指出,可自行参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface HandlerInterceptor {
// 在实际Handler调用之前执行
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;

// 在实际Handler调用之后执行
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;

// 在请求完成之后执行
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}

HandlerExceptionResolver

在 SpringMVC 处理请求映射或者实际调用 Controller 的过程中,如果发生异常,会将异常委托给一组 HandlerExceptionResolver 来处理,通常会给前端返回一个错误响应。

具体的代码实现在 DispatcherServletprocessHandlerException() 方法里面,逻辑如下:

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
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {

// 检查已注册的 HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}

throw ex;
}

@InitBinider

@ModelAttribute

ControllerAdvice

RequestBodyAdvice/ResponseBodyAdvice

qchery wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!