一、Spring MVC基础认知
Spring MVC是什么?
Spring MVC(Model-View-Controller)是Spring框架提供的基于Java的轻量级Web应用开发框架,采用前端控制器模式(Front Controller Pattern)实现请求路由与处理。其核心组件DispatcherServlet作为统一入口,协调整个请求处理链路,实现业务逻辑与视图渲染的解耦。
自Spring 2.5起正式成为Spring Framework官方模块,至今仍是国内Java Web开发主流技术栈之一,尤其在传统企业级项目、银行系统、政务平台等场景中广泛使用。
1.1 MVC分层模型
Spring MVC严格遵循MVC设计模式,各层职责明确:
- Model:数据模型,包含业务数据及状态,通常由POJO类构成;
- View:视图层,负责展示数据(如JSP、Thymeleaf、Freemarker模板);
- Controller:控制器,处理用户请求,调用Service层,返回视图或数据。
通过这种分层,Spring MVC实现了关注点分离(Separation of Concerns),极大提升代码可维护性与可测试性。
1.2 Spring MVC设计原则
核心设计原则
- 松耦合:各组件通过接口协作,降低依赖;
- 高内聚:每个组件职责单一,便于扩展与替换;
- 可插拔:支持自定义HandlerMapping、ViewResolver等组件;
- 非侵入式:控制器无需继承特定基类或实现特定接口。
1.3 为什么选择Spring MVC?
根据2024年Stack Overflow开发者调查及国内主流招聘平台(拉勾、BOSS直聘)数据显示,超过68%的Java后端岗位明确要求掌握Spring MVC或Spring Boot(基于Spring MVC扩展)。其优势体现在:
- 与Spring生态深度整合,无缝使用IoC、AOP、事务管理等特性;
- 注解驱动开发,简化配置,提升开发效率;
- 支持RESTful风格API开发,符合现代微服务架构趋势;
- 内置数据绑定、类型转换、校验(JSR-303)、国际化等完善功能;
- 支持异步请求处理(Servlet 3.0+),提升高并发场景吞吐量。
二、Spring MVC核心组件详解
理解Spring MVC核心组件是掌握其运行机制的关键。以下是九大核心组件及其作用:
DispatchServlet(前端控制器)
DispatcherServlet是整个请求处理流程的中枢,负责接收所有HTTP请求,并协调各组件完成请求处理。它继承自HttpServlet,是Spring MVC的入口点。
关键职责:
- 解析请求URL,定位对应Handler;
- 调用HandlerAdapter执行Controller;
- 处理异常(调用HandlerExceptionResolver);
- 渲染视图(调用ViewResolver);
- 完成响应后清理资源(如LocaleContext、RequestAttributes)。
HandlerMapping(处理器映射器)
HandlerMapping负责根据请求URL查找对应的HandlerExecutionChain(包含Controller及拦截器链)。常见实现类:
| 实现类 | 映射方式 | 适用场景 |
|---|---|---|
RequestMappingHandlerMapping |
基于注解(@RequestMapping等) | Spring MVC 3.1+主流方式 |
BeanNameUrlHandlerMapping |
Bean名称匹配URL | 传统XML配置方式 |
SimpleUrlHandlerMapping |
URL→Bean显式映射 | 动态路由或特殊路径映射 |
实际开发中,95%以上项目使用RequestMappingHandlerMapping,通过@Controller与@RequestMapping注解声明式配置路由。
HandlerAdapter(处理器适配器)
HandlerAdapter负责调用Controller方法并处理参数绑定、返回值解析。其设计符合“适配器模式”,解决不同Handler类型调用差异问题。
关键适配器:
RequestMappingHandlerAdapter:处理注解式Controller(@RequestMapping/@GetMapping等);HttpRequestHandlerAdapter:处理实现HttpRequestHandler接口的Bean;SimpleControllerHandlerAdapter:处理实现Controller接口的传统Bean。
现代项目几乎全部使用RequestMappingHandlerAdapter,其支持方法参数注解(@RequestParam、@PathVariable等)、返回值类型(ModelAndView、String、@ResponseBody)自动转换。
ViewResolver(视图解析器)
ViewResolver负责将逻辑视图名解析为具体视图实现(如JSP、Thymeleaf)。常见配置:
重要特性:支持视图链(View Chain)、视图重定向(RedirectView)、JSON视图(MappingJackson2JsonView)等高级功能。
其他核心组件
① MultipartResolver(文件上传解析器)
处理multipart/form-data请求,实现文件上传。常用实现:CommonsMultipartResolver(基于Apache Commons FileUpload)与StandardServletMultipartResolver(基于Servlet 3.0原生API)。
② LocaleResolver(区域解析器)
支持国际化(i18n),根据请求头、Cookie或Session确定用户区域。常用实现:AcceptHeaderLocaleResolver、SessionLocaleResolver、CookieLocaleResolver。
③ ThemeResolver(主题解析器)
支持动态主题切换(如换肤功能),配合ThemeSource与Theme接口实现。
④ HandlerExceptionResolver(异常解析器)
全局异常处理入口,常用实现:SimpleMappingExceptionResolver(XML配置)与@ControllerAdvice注解(注解式,Spring 3.2+推荐)。
三、Spring MVC请求处理流程深度解析
掌握完整的请求处理链路是面试高频考点。以下以DispatcherServlet为中心,分步骤详解请求处理流程:
DispatcherServlet作为前端控制器拦截所有请求(通常配置为/或*.do)。HandlerMapping,根据请求URL查找对应的HandlerExecutionChain(含Controller及拦截器链)。HandlerAdapter,通过适配器模式调用Controller方法,期间完成参数绑定(@RequestParam)、类型转换、校验(@Valid)等。ModelAndView(含视图名与数据模型)或直接返回String/对象(配合@ResponseBody)。HandlerExceptionResolver链,匹配异常处理器(@ExceptionHandler)。ViewResolver解析逻辑视图名→具体视图对象(如JSP),将Model数据填充到request域,执行视图渲染(如JSP生成HTML)。3.1 请求处理流程图解
标准流程可简化为以下UML序列图逻辑(文字描述):
3.2 关键源码片段解析
以下为DispatcherServlet.doDispatch()核心逻辑简化版(Spring 5.x):
四、Spring MVC配置与优化技巧
4.1 注解驱动配置
现代Spring MVC项目普遍采用Java配置替代XML,结合@EnableWebMvc与WebMvcConfigurer接口实现灵活扩展:
4.2 数据绑定与校验
Spring MVC支持自动将请求参数绑定到对象属性(DataBinder),并集成JSR-303校验框架:
4.3 异常处理最佳实践
推荐使用@ControllerAdvice实现全局异常处理,统一返回结构(如REST API返回JSON):
@ControllerAdvice可结合@Order注解设置优先级;支持方法级异常处理(@ExceptionHandler)与类级异常处理组合使用;RESTful API中推荐统一返回错误码+消息格式。
五、Spring MVC与Spring Boot对比分析
随着Spring Boot兴起,许多开发者产生疑问:Spring MVC是否过时?以下从多个维度对比二者差异:
| 对比维度 | Spring MVC | Spring Boot |
|---|---|---|
| 配置方式 | XML + Java Config混合,配置繁琐 | 自动配置(AutoConfiguration),零配置启动 |
| 内嵌容器 | 需外部部署(如Tomcat WAR包) | 内嵌Tomcat/Jetty/Undertow,可执行JAR |
| 依赖管理 | 需手动管理版本兼容性 | 通过spring-boot-starter统一依赖 |
| 生产就绪功能 | 需额外集成(如Actuator需手动配置) | 开箱即用(健康检查、指标、日志等) |
| 适用场景 | 传统企业级项目、遗留系统维护 | 微服务、云原生应用、快速开发 |
关键结论
- Spring Boot ≠ 替代Spring MVC,而是Spring生态的快速开发平台,其Web模块默认集成Spring MVC;
- 使用Spring Boot开发Web应用时,仍需掌握Spring MVC核心原理(如DispatcherServlet、拦截器机制);
- 企业中70%以上新项目采用Spring Boot,但老系统(如银行核心系统)仍大量使用Spring MVC,二者并存是长期趋势。
六、Spring MVC高频面试问题集锦
以下整理10个真实面试中出现频率最高的问题及专业级回答:
Q1:请详细描述Spring MVC请求处理流程
标准回答:
- 客户端发送HTTP请求至Web容器;
- DispatcherServlet作为前端控制器拦截请求;
- 通过HandlerMapping查找HandlerExecutionChain;
- 调用HandlerAdapter执行Controller;
- Controller调用Service完成业务逻辑;
- 返回ModelAndView;
- ViewResolver解析视图并渲染;
- 最终响应客户端。
进阶补充:可结合源码说明DispatcherServlet.doDispatch()关键步骤;提及拦截器在preHandle/afterCompletion的执行时机;说明异步请求中Callable/DeferredResult的处理机制。
Q2:@RequestMapping与@GetMapping区别
核心区别:@GetMapping是@RequestMapping(method = RequestMethod.GET)的组合注解,属于“元注解”(Meta-Annotation)。
其他组合注解:
| 注解 | 等价于 |
|---|---|
@PostMapping |
@RequestMapping(method = POST) |
@PutMapping |
@RequestMapping(method = PUT) |
@DeleteMapping |
@RequestMapping(method = DELETE) |
@PatchMapping |
@RequestMapping(method = PATCH) |
最佳实践:RESTful API中优先使用组合注解,提高可读性;传统非REST接口可混合使用@RequestMapping。
Q3:如何实现RESTful API
实现要点:
- 使用HTTP动词表示操作(GET查、POST增、PUT改、DELETE删);
- URL使用名词复数(如/users而非/user);
- 通过@PathVariable获取路径参数;
- 使用@RequestBody绑定JSON请求体;
- 返回JSON需配合@ResponseBody或@RestController。
Q4:拦截器与过滤器区别
本质差异:
| 特性 | 过滤器(Filter) | 拦截器(Interceptor) |
|---|---|---|
| 依赖框架 | Servlet规范,不依赖Spring | Spring MVC特有 |
| 执行时机 | 请求进入Servlet前、响应离开前 | 请求进入Controller前后、视图渲染后 |
| 方法数量 | 1个(doFilter) | 3个(preHandle/postHandle/afterCompletion) |
| 作用范围 | 所有请求(包括静态资源) | 仅Spring MVC处理的请求 |
使用场景:过滤器适合处理编码、CORS等全局问题;拦截器适合权限校验、日志记录等业务相关逻辑。
Q5:如何处理文件上传
步骤:
- 添加依赖(spring-web + commons-fileupload或Servlet 3.0+);
- 配置MultipartResolver;
- Controller使用@RequestPart或@RequestBody(MultipartFile);
- 保存文件至服务器或云存储。
Q6:@RequestBody与@RequestParam区别
核心差异:
| 特性 | @RequestBody | @RequestParam |
|---|---|---|
| 数据来源 | 请求体(JSON/XML) | URL参数或表单字段 |
| Content-Type | application/json, application/xml | application/x-www-form-urlencoded, multipart/form-data |
| 转换器 | HttpMessageConverter | PropertyEditor/ConversionService |
| 参数类型 | POJO、Map | 基本类型、String、数组、集合 |
典型错误:用@RequestBody接收表单数据(应使用@RequestParam);未加@RequestBody导致JSON无法反序列化。
Q7:视图跳转方式有哪些
三种主流方式:
- forward(服务器端转发):
- URL不变,共享request域
- 代码:
return "forward:/success"
- redirect(客户端重定向):
- URL改变,request域丢失
- 代码:
return "redirect:/success"
- 视图名(默认forward):
- 需配置ViewResolver解析前缀/后缀
- 代码:
return "success"
注意:redirect时参数需显式拼接(如redirect:/user?id=1);forward可传递Model数据。
Q8:如何避免XSS攻击
防护措施:
- 输出转义:JSP中使用
<c:out>或Thymeleaf默认转义; - 输入校验:后端校验特殊字符(如
<、>); - CSP策略:设置Content-Security-Policy响应头;
- HTML白名单过滤:使用Jsoup等库清洗用户输入。
Q9:异步请求如何实现
两种主流方式:
- Callable(Spring MVC 3.2+):
- 返回Callable对象,由容器单独线程执行
- 适合IO密集型任务
- DeferredResult(Spring MVC 3.2+):
- 异步设置结果,适合跨服务调用
- 可配合WebAsyncManager
Q10:Spring MVC线程安全吗
关键结论:
- Controller默认单例(Spring默认Bean作用域为singleton);
- 线程安全依赖无状态设计:避免使用成员变量存储请求数据;
- 安全实践:
- 所有数据通过方法参数传递
- 使用局部变量
- 需要共享状态时用ThreadLocal或@Scope("prototype")
反例: