code-learning/springmvc/09-Spring MVC 源码分析-请求处理一览.md

887 lines
40 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 精尽 Spring MVC 源码分析 —— 请求处理一览
# 1. 概述
本文,我们来一览一个用户的请求,是如何被 DispatcherServlet 处理的。如下图所示:
> FROM [《Spring MVC 原理探秘 —— 一个请求的旅行过程》](https://www.tianxiaobo.com/2018/06/29/Spring-MVC-原理探秘-一个请求的旅行过程/)
>
> [![img](09-Spring MVC 源码分析-请求处理一览.assets/15300766829012.jpg)](https://blog-pictures.oss-cn-shanghai.aliyuncs.com/15300766829012.jpg)
整体流程实际不复杂,但是涉及的**全部**代码会非常多,所以本文重点在于解析**整体的流程**。特别具体和细节的代码实现,我们会放到后续的文章,一篇一篇细细咀嚼。
## 1.1 如何调试
> 艿艿:自我吐槽,写完之后才发现,忘记提供测试示例了。
比较简单,调试 `org.springframework.web.servlet.DispatcherServletTests` 这个单元测试类,可以运行各种单元测试方法,执行各种情况。😈
# 2. FrameworkServlet
虽然在 [「1. 概述」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 的整体流程图,我们看到请求首先是被 DispatcherServlet 所处理但是实际上FrameworkServlet 才是真正的入门。FrameworkServlet 会实现
- `#doGet(HttpServletRequest request, HttpServletResponse response)`
- `#doPost(HttpServletRequest request, HttpServletResponse response)`
- `#doPut(HttpServletRequest request, HttpServletResponse response)`
- `#doDelete(HttpServletRequest request, HttpServletResponse response)`
- `#doOptions(HttpServletRequest request, HttpServletResponse response)`
- `#doTrace(HttpServletRequest request, HttpServletResponse response)`
- `#service(HttpServletRequest request, HttpServletResponse response)`
等方法。而这些实现,最终会调用 `#processRequest(HttpServletRequest request, HttpServletResponse response)` 方法,处理请求。
## 2.1 不同 HttpMethod 的请求处理
### 2.1.1 service
`#service(HttpServletRequest request, HttpServletResponse response)` 方法,代码如下:
```
// FrameworkServlet.java
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// <1> 获得请求方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// <2.1> 处理 PATCH 请求
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
// <2.2> 调用父类,处理其它请求
} else {
super.service(request, response);
}
}
```
- `<1>` 处,获得请求方法。
- `<2.1>` 处,若请求方法是 `HttpMethod.PATCH` ,调用 `#processRequest(HttpServletRequest request, HttpServletResponse response)` 方法,处理请求。因为 HttpServlet 默认没提供 `#doPatch(HttpServletRequest request, HttpServletResponse response)` 方法,所以只能通过父类的 `#service(...)` 方法,从而实现。另外,关于 processRequest 的详细解析,见 [「2.2 processRequest」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 。
- `<2.2>` 处,其它类型的请求方法,还是调用父类的 `#service(HttpServletRequest request, HttpServletResponse response)` 方法,进行处理。代码如下:
```
// HttpServlet.java
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
```
可能会有胖友有疑惑,为什么不在 `#service(HttpServletRequest request, HttpServletResponse response)` 方法,直接调用 `#processRequest(HttpServletRequest request, HttpServletResponse response)` 方法就好列?因为针对不同的请求方法,处理略微有所不同。
### 2.1.2 doGet & doPost & doPut & doDelete
这四个方法,都是直接调用 `#processRequest(HttpServletRequest request, HttpServletResponse response)` 方法,处理请求。代码如下:
```
// FrameworkServlet.java
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
```
### 2.1.3 doOptions
```
// FrameworkServlet.java
/** Should we dispatch an HTTP OPTIONS request to {@link #doService}?. */
private boolean dispatchOptionsRequest = false;
/**
* Delegate OPTIONS requests to {@link #processRequest}, if desired.
* <p>Applies HttpServlet's standard OPTIONS processing otherwise,
* and also if there is still no 'Allow' header set after dispatching.
* @see #doService
*/
@Override
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 如果 dispatchOptionsRequest 为 true ,则处理该请求
if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
// 处理请求
processRequest(request, response);
// 如果响应 Header 包含 "Allow" ,则不需要交给父方法处理
if (response.containsHeader("Allow")) {
// Proper OPTIONS response coming from a handler - we're done.
return;
}
}
// Use response wrapper in order to always add PATCH to the allowed methods
// 调用父方法,并在响应 Header 的 "Allow" 增加 PATCH 的值
super.doOptions(request, new HttpServletResponseWrapper(response) {
@Override
public void setHeader(String name, String value) {
if ("Allow".equals(name)) {
value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
}
super.setHeader(name, value);
}
});
}
```
- 选读,因为 OPTIONS 请求方法,实际场景下用的少。
- 可参考 [《HTTP 的请求方法 OPTIONS》](https://blog.csdn.net/leikezhu1981/article/details/7402272) 。
### 2.1.4 doTrace
```
// FrameworkServlet.java
/** Should we dispatch an HTTP TRACE request to {@link #doService}?. */
private boolean dispatchTraceRequest = false;
/**
* Delegate TRACE requests to {@link #processRequest}, if desired.
* <p>Applies HttpServlet's standard TRACE processing otherwise.
* @see #doService
*/
@Override
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 如果 dispatchTraceRequest 为 true ,则处理该请求
if (this.dispatchTraceRequest) {
// 处理请求
processRequest(request, response);
// 如果响应的内容类型为 "message/http" ,则不需要交给父方法处理
if ("message/http".equals(response.getContentType())) {
// Proper TRACE response coming from a handler - we're done.
return;
}
}
// 调用父方法
super.doTrace(request, response);
}
```
- 选读,因为 TRACE 请求方法,实际场景下用的少。
- 可参读 [《HTTP Method详细解读(`GET` `HEAD` `POST` `OPTIONS` `PUT` `DELETE` `TRACE` `CONNECT`)》](https://www.cnblogs.com/machao/p/5788425.html) 的 [「9.8 TRACE」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 小节。
## 2.2 processRequest
`#processRequest(HttpServletRequest request, HttpServletResponse response)` 方法,处理请求。代码如下:
```
// FrameworkServlet.java
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// <1> 记录当前时间,用于计算 web 请求的处理时间
long startTime = System.currentTimeMillis();
// <2> 记录异常
Throwable failureCause = null;
// <3> TODO 芋艿
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
// <4> TODO 芋艿
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// <5> TODO 芋艿
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// <6> TODO 芋艿
initContextHolders(request, localeContext, requestAttributes);
try {
// <7> 执行真正的逻辑
doService(request, response);
} catch (ServletException | IOException ex) {
failureCause = ex; // <8>
throw ex;
} catch (Throwable ex) {
failureCause = ex; // <8>
throw new NestedServletException("Request processing failed", ex);
} finally {
// <9> TODO 芋艿
resetContextHolders(request, previousLocaleContext, previousAttributes);
// <10> TODO 芋艿
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
// <11> 打印请求日志,并且日志级别为 DEBUG
logResult(request, response, failureCause, asyncManager);
// <12> 发布 ServletRequestHandledEvent 事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
```
- `<1>` 处,记录当前时间,用于计算 web 请求的处理时间。
- `<2>` 处,记录异常。
- `<3>` 处TODO 1001 Locale
- `<4>` 处TODO 1002 RequestAttributes
- `<5>` 处TODO 1003 Asyn
- `<6>` 处TODO 1001 + 1002
- 【重要】 `<7>` 处,调用 `#doService(HttpServletRequest request, HttpServletResponse response)` **抽象**方法,执行真正的逻辑。代码如下:
```
// FrameworkServlet.java
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
```
- 该**抽象**方法由 DispatcherServlet 实现,所以这就是 DispatcherServlet 处理请求的**真正入口**。详细解析,见 [「3. DispatcherServlet」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 。
- `<8>` 处,记录抛出的异常,最终在 `finally` 的代码段中使用。
- `<9>` 处TODO 1001 + 1002
- `<10>` 处TODO 1001 + 1002
- `<11>` 处,调用 `#logResult(HttpServletRequest request, HttpServletResponse response, Throwable failureCause, WebAsyncManager asyncManager)` 方法,打印请求日志,并且日志级别为 **DEBUG** 。这个方法,感兴趣的胖友,点击 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java#L1079-L1132) 查看。
- `<12>` 处,调用 `#publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause)` 方法,发布 `org.springframework.web.context.support.ServletRequestHandledEvent` 事件。代码如下:
```
// FrameworkServlet.java
/** Should we publish a ServletRequestHandledEvent at the end of each request?. */
private boolean publishEvents = true;
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
long startTime, @Nullable Throwable failureCause) {
// 如果开启发布事件
if (this.publishEvents && this.webApplicationContext != null) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
// 创建 ServletRequestHandledEvent 事件,并进行发布
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
```
- 关于 ServletRequestHandledEvent 的监听使用,可参考 [《使用 Spring ApplicationListener 容器监听器来记录请求信息》](https://my.oschina.net/u/2419285/blog/1510163) 。
> 艿艿:好像到了此处,一直没写到 DispatcherServlet 。😈 有种在啰嗦的感觉,嘿嘿。
# 3. DispatcherServlet
## 3.1 doService
`#doService(HttpServletRequest request, HttpServletResponse response)` 方法DispatcherServlet 的处理请求的入口方法,代码如下:
```
// DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// <1> 打印请求日志,并且日志级别为 DEBUG
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
// <2> TODO 芋艿
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));
}
}
}
// Make framework objects available to handlers and view objects.
// <3> 设置 Spring 框架中的常用对象到 request 属性中
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());
// <4> TODO 芋艿 flashMapManager
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 {
// <5> 执行请求的分发
doDispatch(request, response);
} finally {
// <6> TODO 芋艿
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
```
- `<1>` 处,调用 `#logRequest(HttpServletRequest request)` 方法,打印请求日志,并且日志级别为 DEBUG 。这个方法,感兴趣的胖友,点击 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java#L954-L985) 查看。
- `<2>` 处TODO 1003 芋艿
- `<3>` 处,设置 Spring 框架中的常用对象到 `request` 的属性中。
- `<4>` 处TODO 1004 芋艿 flashMapManager
- `<5>` 处,调用 `#doDispatch(HttpServletRequest request, HttpServletResponse response)` 方法,执行请求的分发。详细解析,见 [「3.2 doDispatch」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 。
- `<6>` 处TODO 1003 芋艿
## 3.2 doDispatch
`#doDispatch(HttpServletRequest request, HttpServletResponse response)` 方法,执行请求的分发。在开始看具体的代码实现之前,我们在来回味下这张图片:
> FROM [《Spring MVC 原理探秘 —— 一个请求的旅行过程》](https://www.tianxiaobo.com/2018/06/29/Spring-MVC-原理探秘-一个请求的旅行过程/)
>
> [![img](09-Spring MVC 源码分析-请求处理一览.assets/15300766829012.jpg)](https://blog-pictures.oss-cn-shanghai.aliyuncs.com/15300766829012.jpg)
- 实际上,这张图,更多的反应的是 DispatcherServlet 的 `#DispatcherServlet(...)` 方法的核心流程。
代码如下:
```
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// <1> TODO 芋艿
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// TODO 芋艿
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// <3> 获得请求对应的 HandlerExecutionChain 对象
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) { // <3.1> 如果获取不到,则根据配置抛出异常或返回 404 错误
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// <4> 获得当前 handler 对应的 HandlerAdapter 对象
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// <4.1> TODO 芋艿 last-modified
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;
}
}
// <5> 前置处理 拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// <6> 真正的调用 handler 方法,并返回视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// <7> TODO 芋艿
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// <8> TODO 芋艿 视图
applyDefaultViewName(processedRequest, mv);
// <9> 后置处理 拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex; // <10> 记录异常
} 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); // <10> 记录异常
}
// <11> 处理正常和异常的请求调用结果。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
// <12> 已完成 拦截器
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
// <12> 已完成 拦截器
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
} finally {
// <13.1> TODO 芋艿,干啥子?
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// <13.2> Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
```
- `<1>` 处TODO 1003 Async
- `<2>` 处,调用 `#checkMultipart(HttpServletRequest request)` 方法,检查是否是上传请求。如果是,则封装成 MultipartHttpServletRequest 对象。详细解析,见 [《精尽 Spring MVC 源码解析 —— MultipartResolver》](http://svip.iocoder.cn/Spring-MVC/MultipartResolver) 中。
- `<3>` 处,调用 `#getHandler(HttpServletRequest request)` 方法,返回请求对应的是 HandlerExecutionChain 对象,它包含处理器( `handler` )和拦截器们( HandlerInterceptor 数组 )。代码如下:
```
// DispatcherServlet.java
/** List of HandlerMappings used by this servlet. */
@Nullable
private List<HandlerMapping> handlerMappings;
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历 HandlerMapping 数组
for (HandlerMapping mapping : this.handlerMappings) {
// 获得请求对应的 HandlerExecutionChain 对象
HandlerExecutionChain handler = mapping.getHandler(request);
// 获得到,则返回
if (handler != null) {
return handler;
}
}
}
return null;
}
```
- 详细的解析,见 [《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(二)之 HandlerInterceptor》](http://svip.iocoder.cn/Spring-MVC/HandlerMapping-2-HandlerInterceptor)
- `<3.1>` 处,如果获取不到,则调用 `#noHandlerFound(HttpServletRequest request, HttpServletResponse response)` 根据配置抛出异常或返回 404 错误。 代码比较简单,胖友点击 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java#L1240-L1257) 自己看该方法。
- `<4>` 处,调用 `#getHandlerAdapter(Object handler)` 方法,获得当前 `handler` 对应的 HandlerAdapter 对象。代码如下:
```
// DispatcherServlet.java
/** List of HandlerAdapters used by this servlet. */
@Nullable
private List<HandlerAdapter> handlerAdapters;
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍历 HandlerAdapter 数组
for (HandlerAdapter adapter : this.handlerAdapters) {
// 判断是否支持当前处理器
if (adapter.supports(handler)) {
// 如果支持,则返回
return adapter;
}
}
}
// 没找到对应的 HandlerAdapter 对象,抛出 ServletException 异常
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
```
- 详细解析,见 [《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(一)之 HandlerAdapter》](http://svip.iocoder.cn/Spring-MVC/HandlerAdapter-1-HandlerAdapter)
- `<4.1>` 处,见 [《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(一)之 HandlerAdapter》](http://svip.iocoder.cn/Spring-MVC/HandlerAdapter-1-HandlerAdapter)
- 【拦截器】`<5>` 处,调用 `HandlerExecutionChain#applyPreHandle(HttpServletRequest request, HttpServletResponse response)` 方法,拦截器的前置处理,即调用 `HandlerInterceptor#preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)` 方法。详细解析,见 [《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(二)之 HandlerInterceptor》](http://svip.iocoder.cn/Spring-MVC/HandlerMapping-2-HandlerInterceptor) 。
- 【Controller】`<6>` 处,调用 `HandlerAdapter#handle(HttpServletRequest request, HttpServletResponse response, Object handler)` 方法,真正的调用 `handler` 方法,并返回视图。这里,一般就会调用我们定义的 Controller 的方法。详细解析,见 TODO 。
- `<7>` 处TODO 1003 Asyn
- `<8>` 处,调用 `#applyDefaultViewName(HttpServletRequest request, ModelAndView mv)` 方法,当无视图的情况下,设置默认视图。代码如下:
```
// DispatcherServlet.java
/** RequestToViewNameTranslator used by this servlet. */
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) { // 无视图
// 获得默认视图
String defaultViewName = getDefaultViewName(request);
// 设置默认视图
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
@Nullable
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
// 从请求中,获得视图
return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
}
```
- 详细解析,见 [《精尽 Spring MVC 源码解析 —— RequestToViewNameTranslator》](http://svip.iocoder.cn/Spring-MVC/RequestToViewNameTranslator) 中。
- 【拦截器】`<9>` 处,调用 `HandlerExecutionChain#applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)` 方法,拦截器的后置处理,即调用 `HandlerInterceptor#postHandle(HttpServletRequest request, HttpServletResponse response, Object handler)` 方法。详细解析,见 [《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(二)之 HandlerInterceptor》](http://svip.iocoder.cn/Spring-MVC/HandlerMapping-2-HandlerInterceptor) 。
- `<10>` 处,记录异常。**注意**,此处仅仅记录,不会抛出异常,而是统一交给 `<11>` 处理。
- `<11>` 处,调用 `#processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)` 方法,处理**正常和异常**的请求调用结果。注意,正常的、异常的,都会进行处理。详细解析,见 [「3.3 processDispatchResult」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 。
- 【拦截器】`<12>` 处,调用 `#triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, Exception ex)` 方法,拦截器的已完成处理,即调用 `HandlerInterceptor#triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)` 方法。详细解析,见 [《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(二)之 HandlerInterceptor》](http://svip.iocoder.cn/Spring-MVC/HandlerMapping-2-HandlerInterceptor) 。
- `<13.1>` 处TODO 1003 Asyn
- `<13.2>` 处,如果是上传请求,则调用 `#cleanupMultipart(HttpServletRequest request)` 方法,清理资源。详细解析,见 [《精尽 Spring MVC 源码解析 —— MultipartResolver》](http://svip.iocoder.cn/Spring-MVC/MultipartResolver) 中。
## 3.3 processDispatchResult
`#processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)` 方法,处理**正常和异常**的请求调用结果。代码如下:
```
// DispatcherServlet.java
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// <1> 标记,是否是生成的 ModelAndView 对象
boolean errorView = false;
// <2> 如果是否异常的结果
if (exception != null) {
// 情况一,从 ModelAndViewDefiningException 中获得 ModelAndView 对象
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
// 情况二,处理异常,生成 ModelAndView 对象
} else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
// 标记 errorView
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// <3.1> 渲染页面
render(mv, request, response);
// <3.2> 清理请求中的错误消息属性
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
// <4> TODO 芋艿
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// <5> 已完成处理 拦截器
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
```
- `<1>` 处,`errorView` 属性,标记是否是生成的 ModelAndView 对象。
- `<2>` 处,如果是否**异常**的结果。
- `<2.1>` 处,情况一,从 ModelAndViewDefiningException 中获得 ModelAndView 对象。
- `<2.2>` 处,情况二,调用 `#processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` 方法,处理异常,生成 ModelAndView 对象。代码如下:
```
// DispatcherServlet.java
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
// 移除 PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE 属性
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
// <a> 遍历 HandlerExceptionResolver 数组,解析异常,生成 ModelAndView 对象
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
// 遍历 HandlerExceptionResolver 数组
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
// 解析异常,生成 ModelAndView 对象
exMv = resolver.resolveException(request, response, handler, ex);
// 生成成功,结束循环
if (exMv != null) {
break;
}
}
}
// 情况一,生成了 ModelAndView 对象,进行返回
if (exMv != null) {
// ModelAndView 对象为空,则返回 null
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex); // 记录异常到 request 中
return null;
}
// We might still need view name translation for a plain error model...
// 设置默认视图
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
// 打印日志
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
// 设置请求中的错误消息属性
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
// 情况二,未生成 ModelAndView 对象,则抛出异常
throw ex;
}
```
- `<a>` 处,遍历 HandlerExceptionResolver 数组,调用 `HandlerExceptionResolver#resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` 方法,解析异常,生成 ModelAndView 对象。详细解析TODO
- `<b>` 处,情况一,生成了 ModelAndView 对象,进行返回。当然,这里的后续代码还有 10 多行,比较简单,胖友自己瞅瞅就 OK 啦。
- `<c>` 处,情况二,未生成 ModelAndView 对象,则抛出异常。
- `<3.1>` 处,调用 `#render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)` 方法,渲染页面。详细解析,见 [「3.4 render」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 。
- `<3.2>` 处,当是 `<2>` 处的情况二时,则调用 `WebUtils#clearErrorRequestAttributes(HttpServletRequest request)` 方法,清理请求中的错误消息属性。为什么会有这一步呢?答案在 `#processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)` 方法中,会调用 `WebUtils#exposeErrorRequestAttributes(HttpServletRequest request, Throwable ex, String servletName)` 方法,设置请求中的错误消息属性。
- `<4>` 处TODO 1003 芋艿
- 【拦截器】`<5>` 处,调用 `#triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, Exception ex)` 方法,拦截器的已完成处理,即调用 `HandlerInterceptor#triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)` 方法。详细解析,见 [《精尽 Spring MVC 源码分析 —— HandlerMapping 组件(二)之 HandlerInterceptor》](http://svip.iocoder.cn/Spring-MVC/HandlerMapping-2-HandlerInterceptor) 。
## 3.4 render
`#render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)` 方法,渲染 ModelAndView 。代码如下:
```
// DispatcherServlet.java
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
// <1> TODO 芋艿 从 request 中获得 Locale 对象,并设置到 response 中
Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
// 获得 View 对象
View view;
String viewName = mv.getViewName();
// 情况一,使用 viewName 获得 View 对象
if (viewName != null) {
// We need to resolve the view name.
// <2.1> 使用 viewName 获得 View 对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) { // 获取不到,抛出 ServletException 异常
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
// 情况二,直接使用 ModelAndView 对象的 View 对象
} else {
// No need to lookup: the ModelAndView object contains the actual View object.
// 直接使用 ModelAndView 对象的 View 对象
view = mv.getView();
if (view == null) { // 获取不到,抛出 ServletException 异常
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
// 打印日志
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
// <3> 设置响应的状态码
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// <4> 渲染页面
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
```
- `<1>` 处,调用 `LocaleResolver#resolveLocale(HttpServletRequest request)` 方法,从 `request` 中获得 Locale 对象,并设置到 `response` 中。详细解析,见 TODO 1001
- `<2>` 处,获得 View 对象。分成两种情况,代码比较简单,胖友自己瞅瞅。
- `<2.1>` 处,调用 `#resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request)` 方法,使用 `viewName` 获得 View 对象。代码如下:
```
// DispatcherServlet.java
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
// 遍历 ViewResolver 数组
for (ViewResolver viewResolver : this.viewResolvers) {
// 根据 viewName + locale 参数,解析出 View 对象
View view = viewResolver.resolveViewName(viewName, locale);
// 解析成功,直接返回 View 对象
if (view != null) {
return view;
}
}
}
// 返回空
return null;
}
```
- 详细解析,见 [《精尽 Spring MVC 源码解析 —— ViewResolver》](http://svip.iocoder.cn/Spring-MVC/ViewResolver)
- `<3>` 处,设置响应的状态码。
- `<4>` 处,调用 `View#render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)` 方法,渲染视图。详细解析,见 [《精尽 Spring MVC 源码解析 —— ViewResolver》](http://svip.iocoder.cn/Spring-MVC/ViewResolver) 。
# 666. 彩蛋
到此,我们已经对 DispatcherServlet 是如何处理请求已经有了整体的认识。当然,我们对每个 Spring MVC 组件,细节暂时没有进行深扣,正如我们在看本文,会有大量的 TODO 。不要方,后面的每一篇,我们会对每个 Spring MVC 组件逐个解析。这样,我们对 Spring MVC 会更加了解。
其实,这也是看源码的套路,先整理,后局部,逐步逐步抽丝剥茧,看清理透。
还有,在看完后续的 Spring MVC 的每个组件后,胖友可以在回过头在重新看看这个文章,是否能够将文章更好的串联在一起。
------
下面,艿艿整理了一些网络上讲述 Spring MVC 处理请求的一些图,帮助我们更好的理解这个过程。
> FROM [《SpringMVC - 运行流程图及原理分析》](https://blog.csdn.net/J080624/article/details/77990164)
>
> **流程示意图**
>
> [![流程示意图](09-Spring MVC 源码分析-请求处理一览.assets/01.png)](http://static.iocoder.cn/images/Spring/2022-02-21/01.png)流程示意图
>
> **代码序列图**
>
> [![代码序列图](09-Spring MVC 源码分析-请求处理一览.assets/02.png)](http://static.iocoder.cn/images/Spring/2022-02-21/02.png)代码序列图
>
> ------
>
> FROM [《看透 Spring MVC源代码分析与实践》](https://item.jd.com/11807414.html) P123
>
> **流程示意图**
>
> [![《流程示意图》](09-Spring MVC 源码分析-请求处理一览.assets/03.png)](http://static.iocoder.cn/images/Spring/2022-02-21/03.png)《流程示意图》
------
参考和推荐如下文章:
- 田小波 [《Spring MVC 原理探秘 - 一个请求的旅行过程》](https://www.tianxiaobo.com/2018/06/29/Spring-MVC-原理探秘-一个请求的旅行过程/)
- 郝佳 [《Spring 源码深度解析》](https://item.jd.com/11311737.html) 的 [「11.3 DispatcherServlet」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 小节
- 韩路彪 [《看透 Spring MVC源代码分析与实践》](https://item.jd.com/11807414.html) 的 [「第10章 Spring MVC 之用」](https://svip.iocoder.cn/Spring-MVC/DispatcherServlet-process-request-intro/#) 小节