Skip to content

Spring MVC

Spring MVC 负责把 HTTP 世界和 Java 方法连接起来。客户端看到的是 URL、Method、Header、Query、Body;业务代码希望拿到的是 Java 对象、业务命令和明确的返回结果。MVC 的工作就是完成这层翻译和调度。

Spring MVC 请求处理流程

详细位置

  • 本页说明 MVC 请求链、Controller 职责、参数绑定、校验、异常处理和排查方式。
  • Controller、Service 等对象如何注入见 IoC 容器
  • Service 方法上的事务和日志增强见 AOP
  • Boot Web 默认配置见 Spring Boot

请求链路

一次接口请求可以按这条链理解:

  1. 请求先到 Servlet 容器。
  2. Filter 处理更底层的通用逻辑,例如编码、安全链、链路追踪。
  3. DispatcherServlet 接管请求,成为 MVC 的统一入口。
  4. HandlerMapping 找到哪个控制器方法能处理这个请求。
  5. HandlerAdapter 完成参数绑定、类型转换和校验。
  6. Controller 调用 Service。
  7. 返回值处理器把 Java 对象写成 JSON 或视图。
  8. 如果出现异常,异常处理器把异常转换成响应。

DispatcherServlet 可以理解为前台调度员:它不亲自处理业务,而是找到合适窗口、准备好材料、交给对应处理人,再把处理结果按协议返回。

分层职责

层级负责不负责
ControllerHTTP 协议适配、参数校验、响应组织复杂业务决策、事务细节、数据库访问
Request / Response接口入参和出参模型持久化细节
Service业务编排、事务边界HTTP Header、URL、状态码细节
Repository / Client数据库、缓存、外部接口访问业务流程编排
Advice / Interceptor通用异常、上下文、拦截逻辑核心业务分支

Controller 像海关窗口,负责检查表单、核对格式、把材料转交给业务部门。它不应该在窗口现场决定库存、支付、审批、结算等复杂规则。

请求映射

请求映射回答三个问题:

  • 这个请求的路径是什么。
  • 这个请求使用什么 HTTP Method。
  • 这个请求需要什么附加条件,例如参数、Header、内容类型。

常见匹配关系:

请求数据MVC 注解适合场景
路径片段@PathVariable/orders/1001 中的 1001
查询参数@RequestParam分页、筛选、搜索关键词
请求体 JSON@RequestBody创建、修改、复杂查询条件
请求头@RequestHeader请求 ID、租户、客户端版本
Cookie@CookieValue会话标识、灰度标记

不清楚用哪个时,可以按数据位置判断:在路径里就是 PathVariable,在问号后就是 RequestParam,在 JSON Body 里就是 RequestBody。

参数绑定与校验

参数绑定是把字符串、JSON、文件等 HTTP 输入转换成 Java 类型。校验是在进入业务前检查输入是否满足基础格式。

适合放在校验层的规则:

  • 字段不能为空。
  • 数字范围是否合法。
  • 字符串长度是否合法。
  • 集合是否为空。
  • 邮箱、手机号等基础格式。

不适合放在校验层的规则:

  • 库存是否足够。
  • 用户是否可以取消订单。
  • 支付状态是否允许退款。
  • 优惠券是否满足业务条件。

后一类属于业务规则,应放在 Service 或领域对象里。校验层负责“材料格式是否齐”,业务层负责“这件事能不能办”。

统一异常

统一异常处理的目标是让接口错误稳定、可读、可排查。

一个合理的错误响应通常包含:

  • 业务错误码。
  • 面向调用方的错误信息。
  • 请求追踪 ID。
  • 必要时包含字段级校验错误。

不应返回给外部的信息:

  • Java 堆栈。
  • 数据库 SQL 细节。
  • 内部文件路径。
  • 密钥、Token、连接串。

HTTP 状态码和业务错误码不冲突。参数错误可以是 400,同时业务错误码是 PARAM_INVALID;系统异常可以是 500,同时业务错误码是 SYSTEM_ERROR

拦截器、过滤器、AOP 的区别

机制所在位置更适合做什么
FilterServlet 容器层,MVC 之前安全链、编码、跨域、链路 ID
InterceptorMVC 层,知道 Handler 信息登录态、租户上下文、接口审计
AOPBean 方法调用层事务、方法级日志、业务注解增强

选择时看你需要的信息在哪一层。如果需要原始请求和响应,用 Filter;如果需要知道目标控制器方法,用 Interceptor;如果关注 Service 方法调用,用 AOP。

CORS

CORS 是浏览器安全策略下的跨域访问控制,不是后端接口本身的业务权限。排查跨域时要同时看四处:

  1. 浏览器控制台报错。
  2. 预检请求 OPTIONS 是否到达服务。
  3. 网关或 Nginx 是否已经处理 CORS。
  4. Spring MVC 或 Spring Security 是否重复或覆盖了响应头。

生产环境应明确允许的 Origin、Method 和 Header。长期使用任意 Origin 会扩大风险,尤其是携带 Cookie 或认证信息时。

文件上传

文件上传比普通 JSON 请求更容易出问题,原因是它涉及请求大小、临时文件、文件名、内容类型、存储路径和后续处理。

基本判断:

  • 大小限制应在网关和应用两层都配置。
  • 文件名不要直接使用客户端传入值作为存储名。
  • 扩展名不能作为唯一校验依据。
  • 上传接收和后续导入处理最好解耦,避免长时间占用请求线程。

静态资源

Spring Boot 可以直接提供 classpath 下的静态资源,也可以通过 MVC 配置映射外部目录。用户上传文件如果要提供访问,需要额外考虑:

  • 是否需要鉴权。
  • 是否可能目录穿越。
  • 是否应该使用对象存储或 CDN。
  • 缓存策略如何设置。
  • 删除或替换文件后访问结果是否一致。

静态资源不是“能访问就结束”,生产环境更关注安全边界和缓存行为。

测试选择

测试目标推荐方式原因
只验证 Controller 映射、参数、响应@WebMvcTest启动范围小,定位 Web 层问题快
验证完整接口链路@SpringBootTest + 随机端口能覆盖真实容器和完整配置
只验证业务规则普通单元测试不需要启动 MVC
验证异常响应格式Web 层测试能确认状态码和错误结构

不要把所有测试都写成完整上下文测试。MVC 层问题和业务规则问题应分开验证。

排查路径

404

优先检查:

  1. URL 和服务上下文路径是否正确。
  2. HTTP Method 是否匹配。
  3. Controller 是否被扫描成 Bean。
  4. 类级路径和方法级路径组合后是否符合预期。
  5. 网关或反向代理是否改写路径。

400

常见原因:

  • JSON 格式错误。
  • Content-Type 不匹配。
  • 参数类型转换失败。
  • 校验规则不通过。
  • 必填参数缺失。

415

说明服务端不支持当前请求体类型。JSON 请求通常应使用 application/json,文件上传通常应使用 multipart/form-data

500

优先看服务端日志中的第一处业务异常。全局异常处理应记录原始异常,响应中只保留调用方需要知道的信息。

总结

Spring MVC 是 HTTP 协议和业务方法之间的调度与翻译层。读 MVC 代码时先看请求链,再看 Controller 是否只做协议适配,最后检查校验、异常、拦截器和返回值处理是否集中。排错时按“路由 -> 参数 -> 校验 -> 拦截链 -> 业务异常 -> 响应转换”的顺序推进。

别急,先让缓存热一下。