Appearance
Spring MVC 工作流程
Spring MVC 采用了前端控制器模式来设计整个请求处理流程,通过 DispatcherServlet 作为中央控制器来协调各个组件工作。下面详细介绍 Spring MVC 的工作流程。
整体流程图
详细工作步骤
客户端发送请求:浏览器或其他客户端向服务器发送 HTTP 请求
前端控制器接收请求:DispatcherServlet 作为前端控制器接收所有请求
处理器映射确定处理器:
- DispatcherServlet 咨询 HandlerMapping,确定由哪个 Controller 来处理请求
- HandlerMapping 根据请求的 URL、HTTP 方法以及其他条件返回 HandlerExecutionChain
- HandlerExecutionChain 包含 Handler 和所有应用于该请求的拦截器
处理器适配器调用控制器:
- DispatcherServlet 使用 HandlerAdapter 调用选中的 Handler(Controller)
- HandlerAdapter 会根据 Controller 的类型适配不同的调用方式(如基于注解的控制器或实现特定接口的控制器)
- 控制器执行业务逻辑,访问模型数据,准备视图数据
控制器处理请求:
- Controller 执行业务逻辑,可能调用 Service 层处理业务
- Controller 可能访问数据库或其他资源
- Controller 准备模型数据,选择视图名称或直接返回响应数据
- 返回 ModelAndView 对象,包含视图名和模型数据
视图解析:
- 如果控制器返回逻辑视图名,DispatcherServlet 使用 ViewResolver 将逻辑视图名解析为实际的 View 对象
- ViewResolver 可能使用不同的视图技术(JSP、Thymeleaf、FreeMarker 等)
- 返回 View 对象给 DispatcherServlet
视图渲染:
- DispatcherServlet 将 Model 数据传递给 View
- View 使用模型数据渲染最终的输出(如 HTML 页面)
返回响应:
- 渲染后的视图作为响应返回给客户端
- 对于 REST API,可能直接返回 JSON/XML 数据而不经过视图渲染步骤
拦截器的工作流程
拦截器在 Spring MVC 流程中的执行点:
preHandle:在 Controller 处理请求前执行
- 返回 true 继续执行下一个拦截器或 Controller
- 返回 false 终止流程,不再调用其他拦截器和 Controller
postHandle:在 Controller 处理请求后、视图渲染前执行
- 可以修改 ModelAndView
- 如果 Controller 抛出异常,不会执行此方法
afterCompletion:在整个请求处理完成后执行
- 视图渲染后执行
- 即使 Controller 抛出异常,也会执行此方法
- 通常用于资源清理
关键组件与责任
组件 | 责任 |
---|---|
DispatcherServlet | 前端控制器,协调整个请求处理流程 |
HandlerMapping | 根据请求选择合适的 Controller |
HandlerAdapter | 适配不同类型的 Controller 调用方式 |
Controller | 处理业务逻辑,准备模型数据 |
ModelAndView | 封装模型数据和视图信息 |
ViewResolver | 解析视图名为具体的 View 实现 |
View | 使用模型数据渲染最终输出 |
核心源码分析
DispatcherServlet 的核心处理逻辑在 doDispatch 方法中:
java
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 {
// 1. 检查是否是文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 2. 确定处理请求的Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. 获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4. 处理Last-Modified头
String method = request.getMethod();
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;
}
}
// 5. 应用前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 6. 实际调用Handler,处理请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 7. 如果视图名为空,设置默认视图名
applyDefaultViewName(processedRequest, mv);
// 8. 应用后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 9. 处理结果,包括视图渲染
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 {
// 10. 清理请求相关资源
if (asyncManager.isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
Spring MVC 流程优化点
- 使用拦截器处理横切关注点:如日志、安全、国际化等
- 配置适当的视图解析器链:根据业务需求配置合适的视图技术
- 利用异步处理提高性能:对于长时间运行的请求使用异步处理
- 合理配置处理器映射:根据URL模式配置最高效的HandlerMapping
- 对静态资源使用专门的处理器:配置DefaultServletHttpRequestHandler处理静态资源
- 适当配置文件上传解析器:根据需求配置文件大小限制、临时目录等