文章目录
前言
在Spring MVC中,请求处理的关键是DispatcherServlet
的doDispatch
方法:
其中包含了@RequestBody,@ResponseBody等注解的解析,参数的适配,方法拦截器的执行,目标方法的执行,返回值处理,以及异常处理等逻辑。
Spring MVC整体请求流程,本篇只简单地进行主要流程的分析,诸如如何解析参数,解析返回值,渲染视图等不在此列。
客户端请求
↓
DispatcherServlet
↓
getHandler(request)
↓
HandlerExecutionChain(包含Handler和拦截器)
↓
调用拦截器 preHandle()
↓
调用 Controller 方法
↓
调用拦截器 postHandle()
↓
返回视图、渲染
↓
调用拦截器 afterCompletion()
一、doDispatch
1.1、检查文件上传
当某个请求到达doDispatch
时,首先会检查其是否为文件格式
:
假设我在controller层中进行文件上传的请求处理:
java
@RestController
public class TestController {
@Autowired
private TestService testService;
@RequestMapping(method = RequestMethod.POST, path = "/test")
public void test(@RequestParam("file") MultipartFile file){
System.out.println(file.getName());
}
}
同时需要在web.xml
中配置:
还需要在
web.xml
所引用的spring.xml
中进行配置:
这样在进入
checkMultipart
方法时,首先会利用multipartResolver
校验该请求的类型是否为文件上传:
调用的是
StandardServletMultipartResolver
的isMultipart
,在该方法中,首先会获取请求头中的 Content-Type,如果用户在表单中使用 <form enctype="multipart/form-data">
,那么 Content-Type 就是:multipart/form-data;然后会忽略大小写判断 Content-Type 是否以 multipart/form-data 开头。 如果满足条件,则会进入
multipartResolver
的resolveMultipart
方法进行解析:
调用的同样是
StandardServletMultipartResolver
的resolveMultipart
,关键代码,区分普通的form-data和文件类型的form-data:
- 普通的form-data存入
multipartParameterNames
集合中。 - 文件类型的form-data存入
multipartFiles
集合中。
如果是文件请求,最后会封装成
MultipartHttpServletRequest
的对象,返回到方法的调用处:
这时拿到的
processedRequest
的类型是MultipartHttpServletRequest
,和最初记录的request
类型不一致,故multipartRequestParsed 的值是true,标记本次请求为上传文件 (反之为false)
1.2、得到Handler
继续向下执行,到DispatcherServlet
的getHandler
,该方法的目的是:根据当前请求 URI,找到对应的 Handler(Controller 方法或对象),并包装为 HandlerExecutionChain(包含拦截器链)。
首先会遍历DispatcherServlet.properties
中的三个默认HandlerMapping
:
再去调用各自的
getHandler
方法,目前案例中是@Controller注解
的模式,所以利用的是RequestMappingHandlerMapping
,其中的关键代码,利用请求路径,去容器启动过程中收集的集合中找有无匹配的
lookupHandlerMethod
,会去pathLookup 集合中找,pathLookup:key存放了请求路径,value存放了注解的信息。
最终找到对应的方法对象并返回:
如果找不到匹配的,会在该方法中直接返回给浏览器404:
getHandler
方法的整体逻辑,除了上图中的根据路径找Handler,还有组装拦截器和跨域请求的逻辑:
java
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 调用子类实现的 getHandlerInternal 方法,根据当前请求查找对应的 Handler(Controller 或 HandlerAdapter)
Object handler = getHandlerInternal(request);
// 如果没有找到对应的 Handler,则尝试获取默认 Handler(可在配置中设置)
if (handler == null) {
handler = getDefaultHandler();
}
// 如果仍然没有找到任何 Handler(也没有默认 Handler),则返回 null,DispatcherServlet 会响应 404
if (handler == null) {
return null;
}
// 如果 handler 是一个字符串(通常表示 Bean 名),通过 Spring 容器获取对应的 Bean 实例
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 确保 request 中缓存了 lookupPath(URI 去除 contextPath 的路径),供拦截器或后续处理使用
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
// 构建 HandlerExecutionChain,它包含了 handler 及所有匹配的 HandlerInterceptor 拦截器链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
// 判断是否 handler 提供了 CORS 配置,或者当前请求是 CORS 的预检请求(OPTIONS 请求)
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
// 获取当前 handler 的 CORS 配置对象
CorsConfiguration config = getCorsConfiguration(handler, request);
// 如果配置了全局 CorsConfigurationSource,则合并全局 CORS 配置与当前配置
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
// 如果存在 CORS 配置,则进行配置合法性校验(如 allowCredentials 是否兼容 allowOrigins)
if (config != null) {
config.validateAllowCredentials();
}
// 将 CORS 拦截器添加到 HandlerExecutionChain 中,形成新的执行链
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
// 返回最终构建的 HandlerExecutionChain,包含 handler 和对应拦截器链,供 DispatcherServlet 后续调用
return executionChain;
}
1.3、Handler适配
在拿到请求路径相对应的Handler后,就会进入getHandlerAdapter
方法进行适配,同样是拿到DispatcherServlet.properties
中的四个默认HandlerAdapter
:
与案例中适配的应该是
RequestMappingHandlerAdapter
:
这一块的代码和Spring AOP的切面适配一样,都是适配器模式的体现。
1.4、执行拦截器preHandle方法
在拿到Handler对应的适配器之后,就会去执行自定义拦截器的preHandle方法(如果有的话),preHandle方法条件不匹配,就直接结束,不会调用目标方法:
注意如果有多个拦截器,preHandle的顺序是从前往后执行 。
并且不满足preHandle定义的条件,还会去执行自定义拦截器链中重写的
AfterCompletion
方法**(顺序是从后往前执行)**。并且在执行的过程中抛出了异常,只会记录日志。
1.5、执行目标方法
如果拦截器链的preHandle方法条件满足,或者没有自定义拦截器,则会使用1.3中找到的适配器,调用其中的handle
,执行目标方法:
首先会初始化一个
ModelAndView
,然后进入关键代码invokeHandlerMethod
执行目标方法的是在
invokeHandlerMethod
的invokeAndHandle
:
invokeAndHandle
:
invokeAndHandle
的invokeForRequest
:先获取请求参数,然后通过反射 调用目标方法:
最后回到
invokeAndHandle
,设置返回状态码,并且使用返回值处理器对返回值进行处理:
1.6、执行拦截器PostHandle方法
在执行完目标方法之后,会执行自定义拦截器重写的PostHandle方法**(从后往前执行)**

1.7、处理异常&渲染视图
如果在上述的执行过程中出现了异常,会被捕获,并且在processDispatchResult
方法中统一处理,该方法还会进行视图的渲染:
processDispatchResult
方法的整体逻辑:
java
// DispatcherServlet 中用于处理处理器执行结果的方法(无论是正常返回 ModelAndView 还是发生异常)
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// 标记是否进入了异常处理视图(比如跳转到 error.jsp 等)
boolean errorView = false;
// 如果 Controller 执行过程中发生了异常
if (exception != null) {
// 如果是特殊的 ModelAndViewDefiningException,说明异常中直接包含了一个要跳转的视图
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView(); // 从异常中取出 ModelAndView
}
else {
// 否则进入标准异常处理流程,由异常解析器(HandlerExceptionResolver)来处理异常,返回 ModelAndView
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception); // 调用异常处理器链
errorView = (mv != null); // 如果异常处理器返回了视图,标记 errorView = true
}
}
// 如果最终拿到了一个 ModelAndView 且没有被清空(wasCleared 表示是否显式清除视图渲染流程)
if (mv != null && !mv.wasCleared()) {
render(mv, request, response); // 执行视图渲染流程(包括视图名解析、模板引擎处理、输出 HTML)
if (errorView) {
WebUtils.clearErrorRequestAttributes(request); // 如果是错误页面,清除 request 中设置的错误信息
}
}
else {
// 如果 ModelAndView 是 null 或者已被清除,说明 controller 自行处理了 response,不需要视图渲染
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
// 如果当前请求是异步处理(比如 @Async 或 DeferredResult 等),则跳过后续操作
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// 在 forward 场景下开启异步处理了,直接 return,不做后续 cleanup
return;
}
// 如果存在 handler,则触发 afterCompletion 回调(对应 HandlerInterceptor 的 afterCompletion 方法)
// 这是请求生命周期的最后一步,无论是否异常都要执行
if (mappedHandler != null) {
// 这里不传异常,表示异常已经处理过(前面已经调用过 processHandlerException)
mappedHandler.triggerAfterCompletion(request, response, null);
}
}