Spring源码分析(8)——SpringMVC的请求流转和拦截器处理过程

当一个HTTP请求到达SpringMVC时,SpringMVC是怎么处理这个请求的呢?SpringMVC拦截器中三个preHandle、postHandle和afterCompletion方法的处理过程又是怎样的呢?

一、SpringMVC的请求流转图

下图是网上到处都能找到的SpringMVC的请求流转图,当一个请求进来时,都会经历这么一个流程。

DispatcherServlet
HandlerMapping
HandlerExecutionChain
HandlerIntercepter
HandlerIntercepter
HandlerIntercepter
Handler
HandlerAdapter
Handler
ModelAndView
Handler
ModelAndView
ViewResolver
View
用户
1、发送请求
2、映射处理,获取请求对应的处理方法和拦截器
返回HandlerExecutionChain
3、找到能调用Handler的Adapter
4、实际Controller的处理方法
5、解析视图
View
6、渲染视图
7、返回响应

二、Handler、HandlerAdapter和HandlerMapping

上图中,出现了几个难以理解的类:Handler、HandlerAdapter、HandlerMapping,这三个特别重要,是理解SpringMVC的核心。

2.1 Handler

先说Handler,它是实际处理HTTP请求的方法,例如Controller中的方法便是一个Handler。Handler是Object类型,所以它可能是一个接口,也可能是一个方法,这个方法格式可能也是变化的。

下面是便是两种Handler:

1
2
3
4
5
6
7
8
9
10
public interface HttpRequestHandler {
void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

@Controller
public class UserController {
@RequestMapping(value = {"/getUser"}, method = {RequestMethod.GET})
public String getUser(Model model) throws Exception {
}
}

2.2 HandlerMapping

HandlerMapping的作用是根据request找到响应的处理器Handler和Interceptors。

例如 :/api/getUser 这个request对应着 getUser() 方法和几个拦截器Interceptors。 这个方法和拦截器共同组成HandlerExecutionChain。

HandlerMapping.java
1
2
3
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

2.3 HandlerAdapter

刚才说Handler可能是一个接口,也可能是一个格式不确定的方法。而SpringMVC调用Handler的代码却是固定的,所以这中间就需要一个适配器来解决这个问题,HandlerAdapter就是这样的解决这个问题的类。

如果将Handler比喻为干活的工具,HandlerMapping用于根据需要干的活找到相应的工具,那HandlerAdapter就是会使用这种工具的人,不同的人可能才会使用不同的工具。

HandlerAdapter有三个方法,supports方法返回此人会不会使用该工具,handle方法是让这个人使用该工具。

HandlerAdapter.java
1
2
3
4
5
6
7
public interface HandlerAdapter {
boolean supports(Object handler);

ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

long getLastModified(HttpServletRequest request, Object handler);
}

三、SpringMVC的请求流转过程

1、使用SpringMVC就会配置DispatcherServlet,而它是实现了Servlet接口的,接口中有一个service方法,这就是一个HTTP请求的主入口。当请求过来时,Tomcat容器会调用该方法。

2、从service一路查看代码,请求依次经过了service->doGet->processRequest->doService->doDispatch的流转,前几个都是在做一些准备工作,这是我们不关心,而在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
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
......
this.service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
......
this.doGet(req, resp);
......
}

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
......
doService(request, response);
......
}

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
doDispatch(request, response);
......
}

3、doDispatch包含了一个HTTP请求的所有流转过程,它主要做了以下事情:

1)检查是不是上传请求,如果是上传请求做一些预处理

2)根据request找到Handler

3)根据Handler找到HandlerAdapter

4)处理GET、HEAD请求的Last-Modified

5)执行相应拦截器的preHandle

6)HandlerAdapter使用Handler处理请求

7)执行相应拦截器的postHandle

8)处理返回结果。包括处理异常、渲染页面、发出完成通知触发Interceptor的afterCompletion

DispatcherServlet.java
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 doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);

// 获取Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}

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

// 处理GET、HEAD请求的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;
}
}

// 执行相应拦截器的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// HandlerAdapter使用Handler处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);

// 执行相应拦截器的postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
......
// 处理返回结果。包括处理异常、渲染页面、发出完成通知触发Interceptor的afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
......
}

四、SpringMVC的拦截器处理过程

HandlerInterceptor有三个方法:preHandle、postHandle和afterCompletion,假如有多个拦截器A、B、C、D顺序排列,这时候每个拦截器的三个方法会是怎样的调用顺序呢?

1、如果ABCD四个拦截器的preHandle方法都返回true,preHandle和postHandle形成了一个U型调用链,afterCompletion的调用顺序与preHandle的调用顺序相反。

A preHandle
B preHandle
C preHandle
D preHandle
controller handle
D postHandle
C postHandle
B postHandle
A postHandle
D afterCompletion
C afterCompletion
B afterCompletion
A afterCompletion

preHandle
postHandle
afterCompletion
 A Interceptor
preHandle
postHandle
afterCompletion
 B Interceptor
preHandle
postHandle
afterCompletion
 C Interceptor
preHandle
postHandle
afterCompletion
 D Interceptor
return true
return true
return true
return true
Controller Handle

2、如果C拦截器的preHandle返回false或者抛异常,preHandle和postHandle形成的U型调用链会立即中断,且不会到达实际的Controller。某个拦截器的preHandle返回true时afterCompletion一定会执行,否则不会执行。

A preHandle
B preHandle
C preHandle
B afterCompletion
A afterCompletion

preHandle
postHandle
afterCompletion
 A Interceptor
preHandle
postHandle
afterCompletion
 B Interceptor
preHandle
postHandle
afterCompletion
 C Interceptor
preHandle
postHandle
afterCompletion
 D Interceptor
return true
return true
抛异常或者
return false
return true
Controller Handle

3、如果C拦截器的postHandle抛异常了,preHandle和postHandle形成的U型调用链会立即中断。但所有拦截器的preHandle都返回true,所以所有afterCompletion全部执行。

A preHandle
B preHandle
C preHandle
D preHandle
controller handle
D postHandle
C postHandle
D afterCompletion
C afterCompletion
B afterCompletion
A afterCompletion

preHandle
postHandle
afterCompletion
 A Interceptor
preHandle
postHandle
afterCompletion
 B Interceptor
preHandle
postHandle
afterCompletion
 C Interceptor
preHandle
postHandle
afterCompletion
 D Interceptor
return true
return true
return true
return true
Controller Handle
抛异常

4、如果C拦截器的afterCompletion抛异常了,SpringMVC捕获了所有Throwable,所以不会中断执行。

A preHandle
B preHandle
C preHandle
D preHandle
controller handle
D postHandle
C postHandle
B postHandle
A postHandle
D afterCompletion
C afterCompletion
B afterCompletion
A afterCompletion

preHandle
postHandle
afterCompletion
 A Interceptor
preHandle
postHandle
afterCompletion
 B Interceptor
preHandle
postHandle
afterCompletion
 C Interceptor
preHandle
postHandle
afterCompletion
 D Interceptor
return true
return true
return true
return true
Controller Handle
抛异常

总结

1、preHandle和postHandle组成一个U型链,链中任意一点返回false或者抛异常,都会立刻终止该链的执行。

2、preHandle返回true的拦截器组成一个倒序的afterCompletion链,且任意afterCompletion抛异常都不会影响剩余afterCompletion的执行。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×