0%

DispatcherServlet

DispatcherServlet前端控制器

配置

DispatcherServlet充当SpringMVC的前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。与其他Servlet一样,DispatcherServlet必须在Web应用程序的web.xml文件中进行配置

web.xml配置

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
<!-- springmvc会过滤掉.html的 导致视图解析器无法找到
如果只是使用jsp资源而未使用html的话可以不配置该项
-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 初始化参数:配置Springmvc配置文件的位置和名称
默认配置文件为:/WEB-INF/<servlet-name>-servlet.xml
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 指定servlet加载顺序,指定的话,tomcat在容器启动的时候就会初始化并加载servlet实例,值越小,越早加载;如果不指定该值,表示调用servlet请求时在初始化并加载servlet实例 -->
<!-- 在程序启动的时候根据contextConfigLocation配置的xml文件开始配置spring应用上下文,初始化组件 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!-- url-pattern匹配:
精确匹配:/a
目录匹配:/*
扩展名匹配:*.do
默认匹配:/
这里要配置为/,使之所有在web.xml中找不到匹配元素的url,都会交给DispatcherServlet来处理
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 处理静态资源 -->
<mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"></mvc:resources>
<mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"></mvc:resources>

serlvet3.0扩展

在servlet3.0中,可以不使用xml配置servlet,容器会在类路径中查找实现ServletContainerInitializer接口的类,会使用它来配置Servlet容器,在spring中提供了该接口的实现SpringServletContainerInitializer,该实现类会去查找实现WebApplicationInitializer接口的类来完成配置任务,AbstractAnnotationConfigDispatcherServletInitializer就是该接口的基础实现,可以通过继承AbstractAnnotationConfigDispatcherServletInitializer类来进行配置DispatcherServlet

WebApplicationInitializer的作用就类似于web.xml,项目启动时会自动执行onStartup方法

需要实现三个方法

1
2
3
4
5
6
7
8
// 将一个或多个路径映射到DispacherServlet
protected abstract String[] getServletMappings();

// 返回带有@Configuration注解的类用来定义ContextLoaderLister创建应用上下文中的bean
protected abstract Class<?>[] getRootConfigClasses();

// 返回带有@Configuration注解的类用来定义DispacherServlet应用上下文中的bean
protected abstract Class<?>[] getServletConfigClasses();

静态资源请求问题

因为DispatcherServlet的<url-pattern>配置的是/,针对的是所有请求,对于一些的静态资源(如.js、.css)等也会经过DispatcherServlet,但是DispatcherServlet是处理动态请求的,无法处理静态资源

可以配置<mvc:default-servlet-handler/>来解决,作用是处理静态资源,会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandle,对进行DispatcherServlet的请求进行筛选,如果发现是没有经过映射的请求,就将请求交给WEB服务器默认的Servlet来处理,否则交由DispatcherServlet来处理

default-servlet-name默认是default,如果不是default需要显式的进行配置(看所使用的web服务器,tomcat是default)

1
<mvc:default-servlet-handler default-servlet-name="default"/>

需要注意的是,配置了<mvc:default-servlet-handler/>之后,@RequestMapping的映射会失效,需要加上<mvc:annotation-driven/>配置

源码分析

先看一下DispatcherServlet结构,其本质就是一个Servlet

DispatcherServlet结构

DispatcherServlet的工作大致可以分为两个部分:一是初始化部分,本质就是一个Servlet,在init方法时会进行初始化,最终调用initStrategies方法;二是对于HTTP请求进行响应,调用doGet和doPost方法,最终调用doDispatch方法

DispatcherServlet处理过程

初始化

1
2
3
4
5
6
// DispatcherServlet继承了FrameworkServlet类,FrameworkServlet继承了HttpServletBean类,HttpServletBean继承了HttpServlet,所以DispatcherServlet本质上是一个Servlet
public class DispatcherServlet extends FrameworkServlet

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware

HttpServletBean的init方法

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
public final void init() throws ServletException {

// Set bean properties from init parameters.
// 获取初始化参数 init-param
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 将当前的servlet类转换为一个BeanWrapper,使得可以用spring的方式来对init-param参数进行注入
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
// 注册自定义属性编辑器
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 空实现
initBeanWrapper(bw);
// 属性注入
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {

throw ex;
}
}

// Let subclasses do whatever initialization they like.
// 调用子类的initServletBean,来进行servletBean的初始化
// org.springframework.web.servlet.FrameworkServlet#initServletBean
initServletBean();
}

FrameworkServlet的initServletBean方法

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
protected final void initServletBean() throws ServletException {   
// 初始化上下文
this.webApplicationContext = initWebApplicationContext();
// 空实现
initFrameworkServlet();
}

// 创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化
protected WebApplicationContext initWebApplicationContext() {
// 这是父容器,获取根上下文,ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,是在ContextLoaderListener初始化过程中建立的
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// 这是子容器
WebApplicationContext wac = null;

if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
// 通过构造器创建的实例
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
// 根上下文作为该上下文的双亲上下文
cwac.setParent(rootContext);
}
// 刷新上下文环境
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
// 根据contextAttribute属性从ServletContext中加载WebApplicationContext
wac = findWebApplicationContext();
}
// 通过上述两种方式都没有找到,则说明不存在WebApplicationContext实例,则进行创建
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}

if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
// 调用DispatcherServlet#initStrategies方法,初始化DispatcherServlet的默认策略配置
onRefresh(wac);
}
// 把当前创建的上下文存到ServletContext中
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);

}

return wac;
}

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();

if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// contextClass使用的是默认的DEFAULT_CONTEXT_CLASS,XmlWebApplicationContext.class
// 通过反射实例化contextClass
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

wac.setEnvironment(getEnvironment());
// 配置双亲上下文为传入的根上下文,parent是在ConetextLoderListener中创建的实例
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
// 初始化spring环境
configureAndRefreshWebApplicationContext(wac);

return wac;
}
配置刷新WebApplicationContext
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
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}

wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 监听器 该监听器在接收到ContextRefreshedEvent事件后,会调用onRefresh方法完成刷新
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}

postProcessWebApplicationContext(wac);
applyInitializers(wac);
// 加载配置文件,这里就是执行org.springframework.context.support.AbstractApplicationContext#refresh方法,与spring中加载上下文是一样的
wac.refresh();
}
onRefresh

用于刷新spring在web功能实现中所必须使用的全局变量

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
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

// 下边的组件如果没有配置默认会读取org.springframework.web.servlet下的DispatcherServlet.properties
protected void initStrategies(ApplicationContext context) {
// 初始化MultipartResolver,获取beanName为multipartResolver的bean,没有默认值,必须手动配置,用于处理上传请求,处理方法是将普通的request包装成MultipartHttpServletRequest,可以直接调用getFile方法获取File
initMultipartResolver(context);
// 初始化LocaleResolver,获取beanName为localeResolver的bean,如果没有默认DispatcherServlet.properties获取
//国际化解析器,使用的地方有两个,一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
initLocaleResolver(context);
// 初始化ThemeResolver,获取beanName为themeResolver的bean,如果没有默认DispatcherServlet.properties获取
//用于解析主题。SpringMVC中一个主题对应 一个properties文件,里面存放着跟当前主题相关的所有资源,如图片、css样式等。SpringMVC的主题也支持国际化
initThemeResolver(context);
// 初始化HandlerMappings,如果配置detectAllHandlerMappings为false的话,只会获取beanName为handlerMapping的bean,否则会找所有类型为HandlerMapping的bean,默认是true,如果没有默认DispatcherServlet.properties获取
//初始化处理器映射器
initHandlerMappings(context);
// 初始化HandlerAdapters,如果配置detectAllHandlerAdapters为false的话,只会获取beanName为handlerAdapter的bean,否则会找所有类型为HandlerAdapter的bean,默认是true,如果没有默认DispatcherServlet.properties获取
//初始化处理器适配器
initHandlerAdapters(context);
// 初始化HandlerExceptionResolvers,如果配置detectAllHandlerExceptionResolvers为false的话,只会获取beanName为handlerExceptionResolver的bean,否则会找所有类型为HandlerExceptionResolver的bean,默认是true,如果没有默认DispatcherServlet.properties获取
// 初始化异常处理器,对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。
initHandlerExceptionResolvers(context);
// 初始化RequestToViewNameTranslator,获取beanName为viewNameTranslator的bean,如果没有默认DispatcherServlet.properties获取
//有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要RequestToViewNameTranslator从request获取ViewName了
initRequestToViewNameTranslator(context);
// 初始化ViewResolvers,如果配置detectAllViewResolvers为false的话,只会获取beanName为viewResolver的bean,否则会找所有类型为ViewResolver的bean,默认是true,如果没有默认DispatcherServlet.properties获取
//ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。
initViewResolvers(context);
// 初始化FlashMapManager,获取beanName为flashMapManager的bean,如果没有默认DispatcherServlet.properties获取
//用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。
initFlashMapManager(context);
}

上述默认值是从DispatcherServlet.properties中取的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 国际化解析器
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
# 主题解析器
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
# HandlerMapping
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
# 处理器适配器
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
# 异常解析器
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
# 策略视图名称转换器
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
# 视图解析器
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
# FlashMap管理器
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
介绍各个组件

LocaleResolver

国际化处理

  • AcceptHeaderLocaleResolver 基于URL参数的配置,可以读取url中locale=zh_CN来控制国际化参数
  • CookieLocaleResolver 基于cookie的配置,可以通过浏览器的cookie设置获取Locale对象
  • SessionLocaleResolver 基于session的配置,可以公国检验session的预置的属性来解析,如果没有则会根据请求头中的accept-language来确定

ThemeResolver

主题处理,根据主题控制网页风格

  • FixedThemeResolver 选择一个固定的主题
  • CookieThemeResolver 用于实现用户所选的主题,以cookie的形式存放在客户端的机器上
  • SessionThemeResolver 用于主题存放在session中

HandlerAdapter

  • HttpRequestHandlerAdapter 仅支持HTTP请求处理器的适配,将HTTP请求对象和响应对象传递给HTTP请求处理器的实现,不需要返回值,主要应用在基于HTTP的远程调用实现上
  • SimpleControllerHandlerAdapter 将HTTP请求适配到一个控制器的实现进行处理
  • AnnotationMethodHandlerAdapter 在3.2被废弃,使用RequestMappingHandlerAdapter,基于注解的实现

HandlerExceptionResolver

进行异常处理

处理请求

请求过来之后进行统一处理,由service()/doGet()/doPost()等方法调用

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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// doPost -> processRequest -> doService -> doDispatch
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

processRequest(request, response);
}

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

long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 提取当前线程的LocaleContext和RequestAttributes,在请求结束后进行重置
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());
// 将当前请求的LocaleContext和RequestAttributes绑定到当前线程,LocaleContextHolder和RequestContextHolder中
initContextHolders(request, localeContext, requestAttributes);

try {
// 实际处理请求
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}

finally {
// 请求结束后恢复原始状态
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
// 发布消息
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}


protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
// include请求,对request中的Attribute进行备份
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
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.
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 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.
// 还原request中的Attribute快照
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}

// 真正的请求处理
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类型的,如果是会将请求类型转为MultipartHttpServletRequest
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
// 获取处理当前请求的处理器,根据请求的URL信息去查找匹配的URL的Handler,如果查找成,并返回一个执行链HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
// 没有找到对应的handler,返回404
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
// 根据当前的handler获取对应的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
// 处理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;
}
}
// 执行拦截器的preHandler方法,按顺序执行,如果执行过程中有拦截器的preHandler方法返回false,则需要执行拦截器的afterCompletion方法,按照反向顺序进行执行 for (int i = this.interceptorIndex; i >= 0; i--)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
// 调用handler处理器逻辑,即业务代码Controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 异步处理直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果没有返回view的视图名称,则采用默认的视图名称,添加前缀、后缀
applyDefaultViewName(processedRequest, mv);
// 执行拦截器的postHandle方法,按照反向顺序进行执行 for (int i = interceptors.length - 1; i >= 0; i--)
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);
}
// 处理返回结果,包括视图渲染,处理异常,以及拦截器的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 {
// 异步请求
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.
// 清除multipart上传的资源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch处理
getHandler
1
2
3
4
5
6
7
8
9
10
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {

HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}

这里就以RequestMappingHandlerMapping为例

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
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取request对应的handler
Object handler = getHandlerInternal(request);
if (handler == null) {
// 如果没有,则使用默认的handler
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
ha.handle

通过适配器去调用Handler的实际逻辑,以RequestMappingHandlerAdapter为例

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
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// Execute invokeHandlerMethod in synchronized block if required.
// 如果需要session内的同步执行
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
// 调用用户逻辑
mav = invokeHandlerMethod(request, response, handlerMethod);
}

if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}

return mav;
}
invokeHandlerMethod
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
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 获取数据绑定 initBinder
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取@SessionAttributes
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

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();

invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 进行方法调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}

return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
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
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, 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);
}
}

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

}

if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// 触发Interceptor.afterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
异常解析processHandlerException
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
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {

// Check registered 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;
}

欢迎关注我的其它发布渠道