处理流程
FrameworkServlet
虽然在上面的整体流程图中,我们看到请求首先是被DispatcherServlet
所处理,但是实际上,FrameworkServlet
才是真正的入口:1
2
3
4
5
6
7
8
9
10
11
12
13
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// <1> 获得请求方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// <2.1> 处理 PATCH 请求
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
// <2.2> 调用父类,处理其它请求
} else {
super.service(request, response);
}
}
当不是PATCH
请求时,会调用父类HttpServlet
的service()
方法,在这个方法中调用由子类FrameworkServlet
实现的doGet()
、doPost()
、doPut()
、doDelete()
等各种方法上,而这些方法最终会调用processRequest()
方法,处理请求:
1 | // FrameworkServlet.java |
DispatcherServlet
DispatcherServlet
实现了FrameworkServlet
的doService()
方法,这个方法中会调用到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
84protected 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); // 检查是否是上传请求
// 1. 获取可处理当前请求的处理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果获取不到,则调用 noHandlerFound 根据配置抛出异常或返回 404
noHandlerFound(processedRequest, response);
return;
}
// 2. 获取可执行处理器逻辑的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod(); // 处理 last-modified 消息头
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;
}
}
// 执行拦截器 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 3. 真正调用处理器逻辑(一般就是在这里调用我们的 Controller 方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果 controller 未返回 view 名称,这里生成默认的 view 名称
applyDefaultViewName(processedRequest, mv);
// 执行拦截器 postHandle 方法
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);
}
// 4. 解析并渲染视图
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);
}
}
}
}
整个过程涉及以下几个核心组件:
DispatcherServlet
:请求入口,负责协调各个组件工作HandlerMapping
:内部维护了一些<访问路径,处理器>映射,负责为请求找到合适的处理器HandlerAdapter
:处理器的适配器。Spring中的处理器的实现多变,比如用户处理器可以实现Controller
接口,也可以用@RequestMapping
注解将方法作为一个处理器等,这就导致 Spring 不止到怎么调用用户的处理器逻辑。所以这里需要一个处理器适配器,由处理器适配器去调用处理器的逻辑ViewResolver
:根据视图名查找获得视图对象View
View
:视图对象用于将模板渲染成html或其他类型的文件。比如InternalResourceView
可将jsp渲染成 html。
完整流程虽然像上面那样,但在目前主流的架构中,前后端已经彻底分离了,所以也就将View移交给了前端,上面的视图解析与渲染步骤已不再需要,而是当Handler(Controller)执行完后,判断方法是否有@ResponseBody
注解,有的话则直接将结果写回给用户。但是由于HTTP是不支持返回Java POJO对象的,所以还需要将结果使用HttpMessageConverter
进行转换后才能返回。
HandlerMapping
根据请求获得相应的处理器和拦截器们(HandlerInterceptor
数组 ),代码如下:
1 | // HandlerMapping.java |
返回的对象类型是HandlerExecutionChain
,它包含处理器(handler
)和拦截器们(HandlerInterceptor
数组),简单代码如下:1
2
3
4
5
6
7
8// HandlerExecutionChain.java
/** 处理器 */
private final Object handler;
/** 拦截器数组 */
private HandlerInterceptor[] interceptors;
这里要注意的是处理器的类型是Object
。
HandlerAdapter
因为处理器handler
的类型是Object
类型,需要有一个调用者来实现handler
是怎么被使用,怎么被执行,而HandlerAdapter
的用途就在于此:1
2
3
4
5
6
7
8
9
10
11
12
13public interface HandlerAdapter {
/** 是否支持该处理器 */
boolean supports(Object handler);
/** 执行处理器,返回 ModelAndView 结果 */
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/** 返回请求的最新更新时间。如果不支持该操作,则返回 -1 即可 */
long getLastModified(HttpServletRequest request, Object handler);
}
HandlerInterceptor
HandlerInterceptor
是Spring MVC中的拦截器接口,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public interface HandlerInterceptor {
/**
* 拦截处理器,在 {@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} 执行完之后,无论成功还是失败,
* 并且,只有 {@link #preHandle(HttpServletRequest, HttpServletResponse, Object)} 执行成功之后,才会被执行
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
总结来说:
preHandle()
方法,按拦截器定义顺序调用,若任一拦截器返回false
,则后面的拦截器不再执行,且Controller方法不再调用,处理请求流程结束postHandle()
方法,按拦截器定义逆序调用,在调用Controller方法成功之后执行afterCompletion()
方法,按拦截器定义逆序调用,只有该拦截器在preHandle()
方法返回true
,Controller方法执行完后才能够被调用(不管是否执行成功),且一定会被调用(过程中出现的异常仅会记录到日志中,不会打断下一个拦截器的afterCompletion()
执行)。
HttpMessageConverter
在Spring MVC中,可以使用@RequestBody
和@ResponseBody
两个注解,分别完成请求报文到对象和对象到响应报文的转换,底层这种灵活的消息转换机制就是通过HttpMessageConverter
完成的。
我们知道,在Servlet标准中,可以用javax.servlet.ServletRequest
接口中的以下方法:1
public ServletInputStream getInputStream() throws IOException;
来得到一个ServletInputStream
,从这个ServletInputStream
中,可以读取到一个原始请求报文的所有内容。同样的,在javax.servlet.ServletResponse
接口中,可以用以下方法:1
public ServletOutputStream getOutputStream() throws IOException;
来得到一个ServletOutputStream
,从这个ServletOutputSteam
中,可以输出Http的响应报文内容。
当请求报文来到Java世界,它会被封装成为一个ServletInputStream
的输入流,供我们读取报文,而响应报文则是通过一个ServletOutputStream
的输出流,来输出响应报文。由于从输入流中只能读取到原始的字符串报文,同样,往输出流中也只能写原始的字符,但在处理业务逻辑时更多都是以一个个对象作为处理维度的,因此在SpringMVC中,由HttpMessageConverter
完成这中间的一个转换工作:
1 | public interface HttpMessageConverter<T> { |
HttpMessageConverter
接口的定义出现了成对的canRead()
+read()
和canWrite()
+write()
方法,而参数中的HttpInputMessage
和HttpOutputMessage
则分别是Spring MVC内部对Http请求报文和响应报文的抽象。下面通过一个例子解释消息转换的过程:1
2
3
4"/string", method = RequestMethod.POST) (value =
public String readString(@RequestBody String string) {
return "Read string '" + string + "'";
}
在SpringMVC进入readString(@RequestBody String string)
方法前,会根据@RequestBody
注解选择适当的HttpMessageConverter
实现类来将请求参数解析到string
变量中,具体来说是使用了StringHttpMessageConverter
类,它的canRead()
方法返回true
,然后它的read()
方法会从请求中读出请求参数,绑定到readString(@RequestBody String string)
方法的string
变量中。
当Spring MVC执行readString(@RequestBody String string)
方法后,由于返回值标识了@ResponseBody
注解,Spring MVC将使用StringHttpMessageConverter
的write()
方法,将结果作为String
值写入响应报文,当然,此时canWrite()
方法返回true
。
RequestResponseBodyMethodProcessor
RequestResponseBodyMethodProcessor
同时实现了HandlerMethodArgumentResolver
和HandlerMethodReturnValueHandler
两个接口,前者是将请求报文绑定到处理方法形参的策略接口,后者则是对处理方法返回值进行处理的策略接口。
对HandlerMethodArgumentResolver
接口的实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// RequestResponseBodyMethodProcessor.java
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Object argument = readWithMessageConverters(webRequest, parameter, parameter.getGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
WebDataBinder binder = binderFactory.createBinder(webRequest, argument, name);
if (argument != null) {
validate(binder, parameter);
}
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
return argument;
}
对HandlerMethodReturnValueHandler
接口的实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// RequestResponseBodyMethodProcessor.java
public boolean supportsReturnType(MethodParameter returnType) {
return returnType.getMethodAnnotation(ResponseBody.class) != null;
}
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException {
mavContainer.setRequestHandled(true);
if (returnValue != null) {
writeWithMessageConverters(returnValue, returnType, webRequest);
}
}
两个接口的实现,分别是以是否有@RequestBody
和@ResponseBody
为条件,然后分别调用HttpMessageConverter
来进行消息的读写。
HandlerExceptionResolver
Spring MVC提供了异常解析器HandlerExceptionResolver
接口,将处理器执行时发生的异常,转换成对应的ModelAndView
结果。代码如下:1
2
3
4
5
6
7
8
9public interface HandlerExceptionResolver {
/**
* 解析异常,转换成对应的 ModelAndView 结果
*/
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
也就是说,如果异常被解析成功,则会返回ModelAndView
对象。