1. 项目概述
Spring MVC作为Java Web开发领域的标杆级框架,其精妙的设计思想与高效的请求处理机制一直是开发者进阶的必修课。今天我想从一个实战派的角度,带大家深入Spring MVC的腹地,看看一个HTTP请求究竟是如何被Spring MVC优雅地拆解、分发、处理并最终生成响应的。
在实际项目中,我们经常会遇到这样的场景:某个请求莫名其妙返回404却找不到原因、拦截器突然失效、响应数据格式与预期不符...这些问题往往源于对Spring MVC核心流程的理解不足。通过这次深度解析,你将获得:
- 完整掌握从DispatcherServlet到HandlerAdapter的完整调用链路
- 理解各核心组件如何协同工作完成请求生命周期
- 学会在关键流程节点植入自定义扩展逻辑
- 具备快速定位和解决Spring MVC相关问题的能力
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心架构解析
2.1 九大核心组件协作模型
Spring MVC的优雅之处在于其清晰的职责划分。让我们先看一张简化版的组件交互图(文字描述):
code复制HTTP请求 -> DispatcherServlet -> HandlerMapping -> HandlerAdapter
-> Controller -> ModelAndView -> ViewResolver -> View -> HTTP响应
这九大组件各司其职:
- DispatcherServlet:前端控制器,所有请求的入口和出口
- HandlerMapping:请求URL到处理器的映射路由器
- HandlerAdapter:实际执行处理器方法的适配器
- HandlerInterceptor:拦截器链(常被忽视但极其重要)
- ViewResolver:视图名称到实际视图对象的解析器
- View:渲染模型数据的视图组件
- LocaleResolver:国际化支持的核心
- ThemeResolver:主题解析器
- MultipartResolver:文件上传处理器
关键认知:Spring MVC采用的是"前端控制器+页面控制器"的经典模式,这与Struts等框架的集中式控制有本质区别。
2.2 组件初始化时序
在Servlet容器启动时,Spring MVC会按特定顺序初始化这些组件:
- 首先初始化MultipartResolver(因为可能影响其他组件)
- 接着初始化Locale/ThemeResolver
- 然后初始化HandlerMappings(注意顺序很重要)
- 随后初始化HandlerAdapters
- 最后初始化HandlerExceptionResolver和ViewResolver
这个初始化顺序在org.springframework.web.servlet.DispatcherServlet的initStrategies方法中有明确规定。理解这点对解决"为什么我的自定义组件没生效"这类问题很有帮助。
3. 请求处理全流程拆解
3.1 阶段一:请求接收与预处理
当HTTP请求到达时,DispatcherServlet的doService()方法首先被调用:
java复制protected void doService(HttpServletRequest request, HttpServletResponse response) {
// 1. 将请求属性设置为可访问
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
// 2. 处理文件上传(如果配置了MultipartResolver)
if (this.multipartResolver != null && isMultipart(request)) {
request = this.multipartResolver.resolveMultipart(request);
}
// 3. 设置本地化上下文
LocaleContext localeContext = this.localeResolver.resolveLocaleContext(request);
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
// 4. 设置主题
if (this.themeResolver != null) {
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
}
}
这个阶段有三个关键点需要注意:
- 文件上传处理是同步操作,大文件上传可能阻塞线程
- 本地化解析会影响后续的消息转换和视图渲染
- 所有预处理结果都存储在请求属性中
3.2 阶段二:处理器映射与拦截
接下来进入doDispatch()的核心逻辑:
java复制protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// 1. 获取处理器执行链(包含拦截器)
HandlerExecutionChain mappedHandler = getHandler(request);
// 2. 获取处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 执行前置拦截器
if (!mappedHandler.applyPreHandle(request, response)) {
return; // 如果前置拦截返回false,直接终止流程
}
// 4. 实际调用处理器方法
ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
