DispatcherServlet解析

SpringMVCDispatcherServlet 源码 Part1

1.流程图概览

流程图

2.时序图

时序图

3.继承层次

  • Hierarchy

image

4. 源码剖析

  • 开门见山

4.1 doGet(),doPost()源码

1
2
3
4
5
6
7
8
9
10
11
12
13
@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);
}
  • 此处重写了doGet()doPost()方法,交由processRequest()处理

4.2 processRequest()源码

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
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 获取先前请求的LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 获取当前请求的LocaleContext,其中保存了当前请求的Locale信息
LocaleContext localeContext = buildLocaleContext(request);

// 获取先前请求的Attributes信息
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 获取当前请求的Attributes信息,其中保存了当前请求的各个属性数据
ServletRequestAttributes requestAttributes =
buildRequestAttributes(request, response, previousAttributes);

// 获取当前请求的WebAsyncManager,这只有在当前请求是请求的异步任务时才会真正用到
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// 注册异步任务的拦截器,如果请求的是异步任务,这个拦截器可以拦截异步任务的前置,后置和异常等情况
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),
new RequestBindingInterceptor());

// 将当前请求的Locale,Attributes信息初始化到对应的ThreadLocal对象中,用于后续使用
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 {
// 在请求完成之后,判断当前请求的Locale和Attributes信息是否需要继承,如果需要继承,
// 则会将Locale信息设置到inheritableLocaleContextHolder中,而将Attributes
// 信息设置到inheritableRequestAttributesHolder中;否则就会移除对应的信息,
// 而只为当前请求的ContextHolder设置相应的属性
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
// 调用已注册的在当前请求被销毁时的回调函数,并且更新Session中当前请求所更新的属性
requestAttributes.requestCompleted();
}

if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
} else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
} else {
this.logger.debug("Successfully completed request");
}
}
}

// 发布请求已经完成的事件,以便对该事件进行监听的程序进行相应的处理
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
  • processRequest()方法主要是对LocaleAttributes信息进行了处理,然后就通过doService()方法对请求再次进行了分发

4.3 doSevice()源码

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
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()
? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed
+ " processing " + request.getMethod() + " request for ["
+ getRequestUri(request) + "]");
}

// 这里主要是判断当前请求是否为include请求,如果是include请求,那么就会将当前请求中的
// 数据都放入一个快照中,在当前请求完成之后,会从该块中中取出数据,然后将其重新加载到
// 当前request中,以便request进行后续的处理。这里默认情况下是会对所有的属性进行处理的,
// 因为cleanupAfterInclude默认值为true,如果将其设置为false,那么就只会对Spring框架
// 相关的属性进行处理
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));
}
}
}

// 这里分别将ApplicationContext,LoacleResolver,ThemeResolver和ThemeSource等
// bean添加到当前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());

// 这里FlashMapManager主要的作用在于当请求如果是重定向的请求,那么可以将一些属性保存在FlashMap
// 中,然后通过FlashMapManager进行管理,从而在重定向之后能够获取到重定向之前所保存的请求
if (this.flashMapManager != null) {
// 在当前请求中获取FlashMap数据,如果不是重定向之后的请求,那么这里获取到的就是空值
FlashMap inputFlashMap =
this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
// 将获取到的FlashMap数据保存在request中
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,
Collections.unmodifiableMap(inputFlashMap));
}
// 设置默认的FlashMap和FlashMapManager
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}

try {
// 这里才是真正的对请求进行分发处理的位置
doDispatch(request, response);
} finally {
// 判断当前请求不是一个异步任务的请求,但是是一个include请求,那么就会重新加载
// 请求之前保存的快照数据
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
  • doSevice()最终的处理在doDispatch(request, response);

4.4 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
122
123
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 {
// 这里判断当前请求是否为一个文件请求,这里的判断方式就是要求当前请求满足两点:①请求
// 方式是POST;②判断contentType是否以multipart/开头。如果满足这两点,那么就认为当前
// 请求是一个文件请求,此时会将当前请求的request对象封装为一个
// MultipartHttpServletRequest对象,这也是我们在定义文件请求的Controller时
// 能够将request参数写为MultipartHttpServletRequest的原因。这里如果不是文件请求,
// 那么会将request直接返回。
processedRequest = checkMultipart(request);
// 这里判断原始request与转换后的request是否为同一个request,如果不是同一个,则说明
// 其是一个文件请求
multipartRequestParsed = (processedRequest != request);
// 这里getHandler()方法就是通过遍历当前Spring容器中所有定义的HandlerMapping对象,
// 通过调用它们的getHandler()方法,看当前的HandlerMapping能否将当前request映射
// 到某个handler,也就是某个Controller方法上,如果能够映射到,则说明该handler能够
// 处理当前请求
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果每个HandlerMapping都无法找到与当前request匹配的handler,那么就认为
// 无法处理当前请求,此时一般会返回给页面404状态码
noHandlerFound(processedRequest, response);
return;
}

// 通过找到的handler,然后在当前Spring容器中找到能够支持将当前request请求适配到
// 找到的handler上的HandlerAdapter。这里需要找到这样的适配器的原因是,我们的handler
// 一般都是Controller的某个方法,其是一个Java方法,而当前request则是一种符合http
// 协议的请求,这里是无法直接将request直接应用到handler上的,因而需要使用一个适配器,
// 也就是这里的HandlerAdapter。由于前面获取handler的时候,不同的HandlerMapping
// 所产生的handler是不一样的,比如ReqeustMappingHandlerMapping产生的handler是一个
// HandlerMethod对象,因而这里在判断某个HandlerAdapter是否能够用于适配当前handler的
// 时候是通过其supports()方法进行的,比如RequestMappingHandlerAdapter就是判断
// 当前的handler是否为HandlerMethod类型,从而判断其是否能够用于适配当前handler。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
// 这里判断请求方式是否为GET或HEAD请求,如果是这两种请求的一种,那么就会判断
// 当前请求的资源是否超过了其lastModified时间,如果没超过,则直接返回,
// 并且告知浏览器可以直接使用缓存来处理当前请求
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;
}
}

// 这里在真正处理请求之前会获取容器中所有的拦截器,也就是HandlerInterceptor对象,
// 然后依次调用其preHandle()方法,如果某个preHandle()方法返回了false,那么就说明
// 当前请求无法通过拦截器的过滤,因而就会直接出发其afterCompletion()方法,只有在
// 所有的preHandle()方法都返回true时才会认为当前请求是能够使用目标handler进行处理的
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 在当前请求通过了所有拦截器的预处理之后,这里就直接调用HandlerAdapter.handle()
// 方法来处理当前请求,并且将处理结果封装为一个ModelAndView对象。该对象中主要有两个
// 属性:view和model,这里的view存储了后续需要展示的逻辑视图名或视图对象,而model
// 中则保存了用于渲染视图所需要的属性
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 如果当前是一个异步任务,那么就会释放当前线程,等待异步任务处理完成之后才将
// 任务的处理结果返回到页面
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

// 如果返回的ModelAndView对象中没有指定视图名或视图对象,那么就会根据当前请求的url
// 来生成一个视图名
applyDefaultViewName(processedRequest, mv);
// 在请求处理完成之后,依次调用拦截器的postHandle()方法,对请求进行后置处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
// 将处理请求过程中产生的异常封装到dispatchException中
dispatchException = new NestedServletException("Handler dispatch failed",
err);
}

// 这里主要是请求处理之后生成的视图进行渲染,也包括出现异常之后对异常的处理。
// 渲染完之后会依次调用拦截器的afterCompletion()方法来对请求进行最终处理
processDispatchResult(processedRequest, response, mappedHandler, mv,
dispatchException);
} catch (Exception ex) {
// 如果在上述过程中任意位置抛出异常,包括渲染视图时抛出异常,那么都会触发拦截器的
// afterCompletion()方法的调用
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
} finally {
// 如果当前异步任务已经开始,则触发异步任务拦截器的afterConcurrentHandlingStarted()方法
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest,
response);
}
} else {
// 如果当前是一个文件请求,则清理当前request中的文件数据
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

这里 doDispatch() 方法是进行请求分发和处理的主干部分,其主要分为如下几个步骤:

  • 判断当前是否为文件请求,如果是,则将 request 对象类型转换为 MultipartHttpServletRequest
  • HandlerMapping 中查找能够处理当前 request 的 HandlerMapping,并且获取能够处理当前请求的 handler
  • 根据获取到的 handler,查找当前容器中支持将当前 request 适配到该 handlerHandlerAdapter
  • 应用容器中所有拦截器的 preHandle() 方法,只有在所有的 preHandle() 方法都通过之后才会将当前请求交由具体的 handler 进行处理;
  • 调用 HandlerAdapter.handle() 方法将 request 适配给获取到的 handler 进行处理;
  • 应用容器中所有拦截器的 postHandle() 方法,以对当前请求进行后置处理;
  • 根据处理后得到的 ModelAndView 对象对视图进行渲染;
  • 应用容器中所有拦截器的 afterCompletion() 方法,以对当前请求进行完成处理。