处理流程

FrameworkServlet

虽然在上面的整体流程图中,我们看到请求首先是被DispatcherServlet所处理,但是实际上,FrameworkServlet才是真正的入口:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
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请求时,会调用父类HttpServletservice()方法,在这个方法中调用由子类FrameworkServlet实现的doGet()doPost()doPut()doDelete()等各种方法上,而这些方法最终会调用processRequest()方法,处理请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
// FrameworkServlet.java

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// ...
try {
doService(request, response); // 执行真正的逻辑
}
//...
}

// 抽象方法,交由子类 DispatcherServlet 实现
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;

DispatcherServlet

DispatcherServlet实现了FrameworkServletdoService()方法,这个方法中会调用到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
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); // 检查是否是上传请求

// 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// HandlerMapping.java

public interface HandlerMapping {

String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

/**
* 获得请求对应的处理器和拦截器们
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

返回的对象类型是HandlerExecutionChain,它包含处理器(handler)和拦截器们(HandlerInterceptor数组),简单代码如下:

1
2
3
4
5
6
7
8
// HandlerExecutionChain.java

/** 处理器 */
private final Object handler;

/** 拦截器数组 */
@Nullable
private HandlerInterceptor[] interceptors;

这里要注意的是处理器的类型是Object

HandlerAdapter

因为处理器handler的类型是Object类型,需要有一个调用者来实现handler是怎么被使用,怎么被执行,而HandlerAdapter的用途就在于此:

1
2
3
4
5
6
7
8
9
10
11
12
13
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);

}

HandlerInterceptor

HandlerInterceptor是Spring MVC中的拦截器接口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface HttpMessageConverter<T> {

boolean canRead(Class<?> clazz, MediaType mediaType);

boolean canWrite(Class<?> clazz, MediaType mediaType);

List<MediaType> getSupportedMediaTypes();

T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;

void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;

}

HttpMessageConverter接口的定义出现了成对的canRead()+read()canWrite()+write() 方法,而参数中的HttpInputMessageHttpOutputMessage则分别是Spring MVC内部对Http请求报文和响应报文的抽象。下面通过一个例子解释消息转换的过程:

1
2
3
4
@RequestMapping(value = "/string", method = RequestMethod.POST)
public @ResponseBody 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将使用StringHttpMessageConverterwrite()方法,将结果作为String值写入响应报文,当然,此时canWrite()方法返回true

RequestResponseBodyMethodProcessor

RequestResponseBodyMethodProcessor同时实现了HandlerMethodArgumentResolverHandlerMethodReturnValueHandler两个接口,前者是将请求报文绑定到处理方法形参的策略接口,后者则是对处理方法返回值进行处理的策略接口。

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
9
public interface HandlerExceptionResolver {

/**
* 解析异常,转换成对应的 ModelAndView 结果
*/
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

}

也就是说,如果异常被解析成功,则会返回ModelAndView对象。

参考资料