概述

SpringMVC是一种web层的mvc框架,用于替代Servlet,主要用于处理和相应请求,获取表单参数,表单校验。使用SpringMVC可以简化编程。

SpringMVC底层的执行流程

首先通过一张流传广泛的图,来了解一下大致的流程。
1rJAu4.png

从这张图可以看出,整个请求的入口是DispatcherServlet类。之所以请求的入口会是这个类是因为我们在配置springmvc的时候在web.xml中,将所有的请求都交由DispatcherServlet处理了。
请求到达DispatcherServlet类首先由void doService(HttpServletRequest request, HttpServletResponse 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
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//省略了打印日志的代码

//attributesSnapshot一个用于保存请求快照的map
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
//如果包含请求,就将request中的所有的属性存放到map中
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}

//将一些对象设置到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());


/*FlashMap中保存的是上次转发请求中的属性*/
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
/*首先判断FlashMap中是否有数据,如果有数据就先设置到inputFlashMap中
*/
if (inputFlashMap != null) {
/*如果inputFlashMap不为空,一般代表上一次请求(一般是重定向)
中设置了参数,那么就吧这些参数存放到当前请求的FlashMap中
*/
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
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);
}
}
}
}

通过对doService方法源码的分析,我们可以知道整个方法所做了三件事情:

  1. 快照请求中的属性到attributesSnapshot
  2. 将一些组件设置到request中,方便之后使用
  3. 处理FlashMap
  4. 调用doDispatch(request, response)
  5. doDispatch(request, response) 调用完成后利用attributesSnapshot对request中的属性进行还原

doService还没有真正的进行请求的处理,它所做的事情只是一些准备工作。

下面我们看doDispatch(request, 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
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;
}

// 根据Handler找到对应的HandleAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
//获取请求的类型
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 用HandlerAdapter处理请求,返回ModleAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

//设置默认视图名字
applyDefaultViewName(request, mv);
//应用拦截器的后置处理方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//结果处理
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//触发完成后的回调
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
//触发出现错误的回调
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

doDispatch的执行步骤大致可以分为三个部分:

  1. 根据request找到对应的Handler
  2. 根据找到的Handler找到对应的HandlerAdapter
  3. 用HandlerAdapter调用Handler处理请求
  4. 调用processDispatcheResult方法处理Handler处理之后的结果

这4个步骤内部又完成了许多非常复杂的操作。下面我们就依次解析。

1. doDispatch是如何通过request找到对应的Handler的?
寻找对应的Handler实际上是由mappedHandler = getHandler(processedRequest);完成的。

它的具体实现如下:

1
2
3
4
5
6
7
8
9
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

去掉了打印日志的代码后,逻辑显得非常的简单。遍历handlerMappings,尝试从每一个HandlerMapping获取handler,一旦拿到就直接返回。
这里又有一个疑问:handlerMappings到底是什么?
它的声明如下:

1
2
/** List of HandlerMappings used by this servlet */
private List<HandlerMapping> handlerMappings;

HandlerMapping其实比较复杂。它的整个架构设计图如下:
1rOyIU.png
它的顶层接口HandlerMapping如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface HandlerMapping {
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";

String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

简单的讲就是HandlerMapping中的getHandler方法会返回一个HandlerExecutionChain对象,该对象封装了一个Handler处理对象和一些interctptors(拦截器)。
下面是一个HandlerExecutionChain对象的属性:

1
2
3
4
private final Object handler;
private HandlerInterceptor[] interceptors;
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;

2. doDispatch是如何通过Handler找到HandlerAdapter的,以及什么是HandlerAdapter?

实现用Handler获取HandlerAdapter的代码如下:

1
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

它的具体实现如下:

1
2
3
4
5
6
7
8
9
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

去掉打印日志的方法后,我们可以发现,这个方法和根据request获取Handler的方法非常的一致。因此我们将重点放在:什么是HandlerAdapter上。
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;

long getLastModified(HttpServletRequest request, Object handler);

}

之所以需要HandlerAdapter,是因为Handler的格式是不固定的,所以处理请求的时候需要HandlerAdapter做适配。

拿到HandlerAdapter,就可做处理请求了。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
处理请求后就拿到了一个ModelAndView对象。

具体的代码实现如下:

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

//最终拿到了我们的Controller类
Class<?> clazz = ClassUtils.getUserClass(handler);
//判断是否使用了@SessionAttributes
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}

if (annotatedWithSessionAttributes) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
// Prepare cached set of session attributes names.
}
else {
// 禁用缓存
checkAndPrepare(request, response, true);
}

// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}

return invokeHandlerMethod(request, response, handler);
}

最终又来到了invokehandlerMethod方法了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
//获取处理请求的方法
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
//创建各种组件
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();

//调用方法拿到结果
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
//获取ModelAndView
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
//更新view中的属性
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}

最后调用mappedHandler.applyPostHandle(processedRequest, response, mv);进行后处理。后处理的过程就是调用所有的后置拦截器进行处理。