架构

DispatcherServlet 的工作流程

Spring MVC 一共有九大核心组件,分别是:

  • MultipartResolver
  • LocaleResolver
  • ThemeResolver
  • HandlerMapping
  • HandlerAdapter
  • HandlerExceptionResolver
  • RequestToViewNameTranslator
  • ViewResolver
  • FlashMapManager

发送请求

用户向服务器发送 HTTP 请求,请求被 Spring MVC 的调度控制器 DispatcherServlet 捕获。

映射处理器

DispatcherServlet 根据请求 URL ,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。

处理器适配

DispatcherServlet 根据获得的 Handler,选择一个合适的HandlerAdapter 。(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 #preHandler(...) 方法)。

提取请求 Request 中的模型数据,填充 Handler 入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:

  • HttpMessageConverter :会将请求消息(如 JSON、XML 等数据)转换成一个对象。
  • 数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等。
  • 数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等。
  • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中。

Handler(Controller) 执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象。

对于目前主流的架构,前后端已经进行分离了,所以 Spring MVC 只负责 nodel和 controller两块,而将 view移交给了前端。所以,在上图中的步骤 ⑤ 和 ⑥ 两步,已经不在需要。

流程示意图

流程示意图

《流程示意图》

WebApplicationContext 是实现ApplicationContext接口的子类,专门为 WEB 应用准备的。

Spring MVC 拦截器

org.springframework.web.servlet.HandlerInterceptor

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
// HandlerInterceptor.java

/**
* 拦截处理器,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行之前
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}

/**
* 拦截处理器,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行成功之后
*/
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}

/**
* 拦截处理器,在 {@link HandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)} 执行完之后,无论成功还是失败
*
* 并且,只有该处理器 {@link #preHandle(HttpServletRequest, HttpServletResponse, Object)} 执行成功之后,才会被执行
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
  • #preHandle(...) 方法,调用 Controller 方法之执行。
  • #postHandle(...) 方法,调用 Controller 方法之执行。
  • #afterCompletion(...) 方法,处理完 Controller 方法返回结果之执行

作用:

  • 记录访问日志。
  • 记录异常日志。
  • 需要登陆的请求操作,拦截未登陆的用户。

Spring MVC 的拦截器和 Filter 过滤器有什么差别

  • 功能相同:拦截器和 Filter都能实现相应的功能,谁也不比谁强。
  • 容器不同:拦截器构建在 Spring MVC 体系中;Filter 构建在 Servlet 容器之上。
  • 使用便利性不同:拦截器提供了三个方法,分别在不同的时机执行;过滤器仅提供一个方法,当然也能实现拦截器的执行时机的效果,就是麻烦一些。

REST

REST 代表着抽象状态转移,它是根据 HTTP 协议从客户端发送数据到服务端

有一些HTTP方法,如:GET,不管你使用多少次它都能产生相同的结果,在没有任何一边影响的情况下,发送多个 GET 请求到相同的URI 将会产生相同的响应结果。因此,这就是所谓幂等操作。

REST 通常不是安全的,但是您可以通过使用 Spring Security 来保护它。

源码环境搭建

1
git clone https://github.com/spring-projects/spring-framework

关系图

代码量

代码统计

容器初始化

Root WebApplicationContext 容器

原始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
<!-- 省略非关键的配置 -->

<!-- [1] Spring配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/applicationContext.xml</param-value>
</context-param>

<!-- ====================================== -->

<!-- [2] Spring MVC配置 -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认
</init-param>
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
  • [1] 处,配置了 org.springframework.web.context.ContextLoaderListener 对象。这是一个 javax.servlet.ServletContextListener 对象,会初始化一个Root Spring WebApplicationContext 容器。
  • [2] 处,配置了 org.springframework.web.servlet.DispatcherServlet 对象。这是一个 javax.servlet.http.HttpServlet 对象,它除了拦截我们制定的 *.do 请求外,也会初始化一个属于它的 Spring WebApplicationContext 容器。并且,这个容器是以 [1] 处的 Root 容器作为父容器

Servlet WebApplicationContext 容器

Servlet WebApplicationContext 容器的初始化,是在 DispatcherServlet 初始化的过程中执行。

类图

Servlet 3.0 集成

https://www.cnkirito.moe/servlet-explore/

一位大佬。。。

Spring Boot 集成

组件

DispatcherServlet初始化 Spring MVC 的各种组件。

1
#initStrategies(ApplicationContext context)
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
// DispatcherServlet.java

//请求的解析器接口
@Nullable
private MultipartResolver multipartResolver;

//本地化( 国际化 )解析器接口
@Nullable
private LocaleResolver localeResolver;

//主题解析器接口
@Nullable
private ThemeResolver themeResolver;

//=====处理器匹配接口
@Nullable
private List<HandlerMapping> handlerMappings;

//====处理器适配器接口
@Nullable
private List<HandlerAdapter> handlerAdapters;

//====处理器异常解析器接口
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;

//请求到视图名的转换器接口
@Nullable
private RequestToViewNameTranslator viewNameTranslator;

//负责重定向时,保存参数到临时存储中
@Nullable
private FlashMapManager flashMapManager;

//实体解析器接口
@Nullable
private List<ViewResolver> viewResolvers;

@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
// 初始化 MultipartResolver
initMultipartResolver(context);
// 初始化 LocaleResolver
initLocaleResolver(context);
// 初始化 ThemeResolver
initThemeResolver(context);
// 初始化 HandlerMappings
initHandlerMappings(context);
// 初始化 HandlerAdapters
initHandlerAdapters(context);
// 初始化 HandlerExceptionResolvers
initHandlerExceptionResolvers(context);
// 初始化 RequestToViewNameTranslator
initRequestToViewNameTranslator(context);
// 初始化 ViewResolvers
initViewResolvers(context);
// 初始化 FlashMapManager
initFlashMapManager(context);
}

请求处理

入口

1
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) 方法,处理请求。

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
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

long startTime = System.currentTimeMillis();
Throwable failureCause = null;

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

initContextHolders(request, localeContext, requestAttributes);

try {
//====该抽象方法由 DispatcherServlet 实现,所以这就是 DispatcherServlet 处理请求的真正入口。
doService(request, response);
}
catch (ServletException | 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();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
1
DispatcherServlet

#doService(HttpServletRequest request, HttpServletResponse response) 方法,DispatcherServlet 的处理请求的入口方法

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
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
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.
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.
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());

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 {
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);
}
}
}
}

1
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
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 {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}

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

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

applyDefaultViewName(processedRequest, mv);
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);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
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.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

1
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
40
41
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {

boolean errorView = false;

if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
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 (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}

if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}

if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}

组件源码解析

HandlerMapping

  • AbstractHandlerMapping
1
2
3
4
5
6
7
8
9
10
public interface HandlerMapping {

...

//获得请求对应的处理器和拦截器们
//返回的对象类型是 HandlerExecutionChain ,它包含处理器( handler )和拦截器们( HandlerInterceptor 数组 )。
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

17个子类,不一一研究了…

1
AbstractHandlerMapping

实现 HandlerMapping、Ordered、BeanNameAware 接口,继承 WebApplicationObjectSupport 抽象类,HandlerMapping 抽象基类,实现了【获得请求对应的处理器和拦截器们】的骨架逻辑,而暴露 #getHandlerInternal(HttpServletRequest request) 抽象方法,交由子类实现。

1
MatchableHandlerMapping
1
2
3
4
5
6
7
8
9
10
11
12
public interface MatchableHandlerMapping extends HandlerMapping {

/**
* Determine whether the given request matches the request criteria.
* @param request the current request
* @param pattern the pattern to match
* @return the result from request matching, or {@code null} if none
*/
@Nullable
RequestMatchResult match(HttpServletRequest request, String pattern);

}

定义判断请求和指定 pattern 路径是否匹配的接口方法

1
DispatcherServlet

在 DispatcherServlet 中,通过调用 #initHandlerMappings(ApplicationContext context) 方法,初始化 HandlerMapping 们。

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
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;

if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}

// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}

HandlerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public interface HandlerAdapter {

//是否支持该处理器
boolean supports(Object handler);

//执行处理器,返回 ModelAndView 结果
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

// 返回请求的最新更新时间。如果不支持该操作,则返回 -1 即可
long getLastModified(HttpServletRequest request, Object handler);

}