Appearance
Spring MVC
Spring MVC 是 Spring 体系里最常见的 Web 框架之一。
很多人对它的印象停留在:写 Controller,收参数,返回 JSON。
这当然没错,但还是太表面。Spring MVC 真正解决的是:一个 HTTP 请求进入 Java Web 应用之后,怎么被接收、路由、绑定参数、调用业务方法,并最终返回响应。
这篇主要看:
Spring MVC到底是什么- 它解决什么问题
- MVC 在这里具体是什么意思
- 一次请求在框架里是怎么流动的
- 工程上最容易混的点有哪些
1. Spring MVC 到底是什么
Spring MVC 直接看成:Spring 体系里的 Web MVC 框架。
它主要负责处理:
- HTTP 请求接入
- URL 路由分发
- 参数绑定
- 调用控制层方法
- 返回页面或 JSON 响应
所以它不是整个 Spring,也不是整个 Boot,而是 Spring 在 Web 层的核心实现。
2. MVC 到底是什么意思
MVC 是:
ModelViewControllerController:接请求、调业务、组织返回Model:承载业务数据View:负责展示结果
在现代前后端分离项目里,View 往往不再是后端模板页面,而更多变成:直接返回 JSON,由前端自己渲染。
所以很多人虽然常写的是 @RestController,但底层仍然是 MVC 这条主线的延伸。
3. Spring MVC 主要解决什么问题
3.1 请求怎么找到正确方法
一个请求进来后,总得先知道:到底该由哪个 Controller、哪个方法来处理。
Spring MVC 负责做这件事。
3.2 参数怎么自动绑定
请求里可能带:
- 路径参数
- Query 参数
- 表单参数
- JSON 请求体
- Header
如果每次都手动解析,开发成本会很高。
Spring MVC 把这些绑定规则统一起来。
3.3 返回结果怎么统一处理
有的方法返回页面,有的方法返回 JSON,有的方法返回状态码和统一响应体。
Spring MVC 会把这些输出流程也统一起来。
3.4 异常怎么统一处理
如果业务方法里抛异常,不能每个 Controller 都自己手写一套兜底。
Spring MVC 提供统一异常处理机制。
4. 一次请求在 Spring MVC 里是怎么流动的
这是最核心的主线。
可以按这条链路理解:
- 请求先进入
DispatcherServlet - 它去找谁能处理这个请求
- 找到对应的
Controller方法 - 做参数绑定和必要转换
- 调用目标方法
- 拿到返回值
- 再把返回值转换成页面或 JSON 响应
这里最关键的角色是 DispatcherServlet。
如果你想继续把“请求进入应用之后,到底经过谁、最后落到谁”这条线看清,可以接着看:
把它画成流程图,大致就是这样:
mermaid
flowchart TD
A[HTTP 请求进入应用] --> B[DispatcherServlet]
B --> C[HandlerMapping 查找处理器]
C --> D[定位到 Controller 方法]
D --> E[HandlerAdapter 负责调用]
E --> F[参数解析与数据绑定]
F --> G[调用 Controller 方法]
G --> H{返回值类型}
H -->|页面| I[ViewResolver 解析视图]
H -->|JSON 或响应体| J[HttpMessageConverter 转换响应]
I --> K[返回 HTTP 响应]
J --> K5. DispatcherServlet 是什么
DispatcherServlet 可以理解成 Spring MVC 的前端控制器。
这里的“前端”不是浏览器前端,而是:所有 Web 请求进入 Spring MVC 时的统一入口。
它解决的是 请求不要直接散着进各个 Controller,而是先经过一个统一调度中心。
这也是为什么它叫 Dispatcher,就是“分发器”。
6. HandlerMapping、HandlerAdapter 这些词到底在说什么
这些名词很容易把人绕晕,但只要抓住角色就没那么可怕。
6.1 HandlerMapping
它解决的是 这个请求该映射到哪个处理器。
可以简单理解成“找人”。
6.2 HandlerAdapter
它解决的是 找到人之后,怎么用统一方式把这个处理器真正调起来。
可以简单理解成“负责调用”。
所以整条链路可以这么记:
DispatcherServlet负责统一接入HandlerMapping负责找处理器HandlerAdapter负责实际调用
7. Controller 和 RestController 有什么区别
7.1 @Controller
它更偏传统 MVC 里的控制层。
在返回页面模板场景里很常见。
7.2 @RestController
它可以理解成 @Controller + @ResponseBody
它更偏:方法返回值直接作为响应体输出,而不是去解析视图。
这也是为什么前后端分离项目里,@RestController 特别常见。
一句话压缩:
@Controller更偏页面控制器@RestController更偏接口控制器
7.3 @RestController 为什么说是组合注解
这是一个很值得单独记住的点。
@RestController = @Controller + @ResponseBody
它不是凭空多出来的新能力,而是把:
- 这个类是控制器
- 方法返回值直接写入响应体
这两个动作打包在一起。
例如:
java
/**
* 用户查询接口示例。
*/
@RestController
@RequestMapping("/users")
public class UserController {
/**
* 按用户 ID 查询用户信息。
*
* @param id 用户主键 ID
* @return 查询到的用户视图对象
*/
@GetMapping("/{id}")
public UserVO getById(@PathVariable Long id) {
return new UserVO(id, "tom");
}
}如果不用 @RestController,通常要这样写:
java
/**
* 使用 `@Controller + @ResponseBody` 暴露 JSON 接口的控制器示例。
*/
@Controller
@ResponseBody
public class UserController {
}8. 参数绑定到底在帮你做什么
Spring MVC 一个很重要的价值,是把请求里的不同参数来源统一绑定到方法参数上。
例如常见的:
@PathVariable@RequestParam@RequestBody@RequestHeader
它们分别在解决不同来源的数据怎么进入方法。
例如:
java
/**
* 演示 Spring MVC 如何把路径参数和查询参数同时绑定到方法参数上。
*
* @param id 路径里的用户 ID
* @param name 查询参数里的用户名称,允许不传
* @return 这里为了突出参数绑定主线,示例里返回 `null`
*/
@GetMapping("/user/{id}")
public UserVO getUser(
@PathVariable Long id,
@RequestParam(required = false) String name) {
return null;
}这里最重要的理解不是注解会写,而是 Spring MVC 在帮你做请求数据到 Java 方法参数之间的映射。
8.1 请求映射相关常用注解
这部分在实际开发里最常用:
| 注解 | 作用 |
|---|---|
@RequestMapping | 通用请求映射注解 |
@GetMapping | 处理 GET 请求 |
@PostMapping | 处理 POST 请求 |
@PutMapping | 处理 PUT 请求 |
@DeleteMapping | 处理 DELETE 请求 |
@PatchMapping | 处理 PATCH 请求 |
这些 xxxMapping 都可以理解成 对 @RequestMapping(method = ...) 的组合封装。
例如:
java
/**
* 使用通用 `@RequestMapping` 处理 GET 请求的写法。
*
* @param id 路径参数中的用户 ID
* @return 这里为了突出注解写法,示例里返回 `null`
*/
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public UserVO getUser(@PathVariable Long id) {
return null;
}通常会写成更直接的:
java
/**
* 使用 `@GetMapping` 处理 GET 请求的更简洁写法。
*
* @param id 路径参数中的用户 ID
* @return 这里为了突出映射写法,示例里返回 `null`
*/
@GetMapping("/users/{id}")
public UserVO getUser(@PathVariable Long id) {
return null;
}8.2 参数绑定常用注解总表
| 注解 | 作用 | 常见场景 |
|---|---|---|
@PathVariable | 绑定路径参数 | /users/{id} |
@RequestParam | 绑定查询参数或表单参数 | ?page=1 |
@RequestBody | 绑定请求体 | JSON 请求 |
@RequestHeader | 绑定请求头 | token、traceId |
@CookieValue | 绑定 Cookie | 登录态、偏好信息 |
再看一个更完整的接口示例:
java
/**
* 演示一个更完整的订单更新接口参数绑定过程。
*
* @param orderId 路径参数里的订单 ID
* @param dryRun 查询参数,表示这次是否只做演练不真正落库
* @param traceId 请求头里的链路追踪 ID
* @param request 请求体里的订单更新内容
* @return 组装后的订单视图对象
*/
@PostMapping("/orders/{orderId}")
public OrderVO updateOrder(
@PathVariable Long orderId,
@RequestParam(defaultValue = "false") boolean dryRun,
@RequestHeader("X-Trace-Id") String traceId,
@RequestBody UpdateOrderRequest request) {
return new OrderVO(orderId, request.getStatus(), dryRun, traceId);
}9. HttpMessageConverter 是什么
当前后端分离项目返回 JSON 时,经常会遇到这个角色。
它可以看成 负责在 Java 对象和 HTTP 消息体之间做转换的组件。
它解决的是:
- 请求体里的 JSON 怎么转成 Java 对象
- Java 返回对象怎么转成 JSON 响应
所以很多“为什么对象能自动转 JSON”的问题,背后其实就和消息转换器有关。
例如下面这个接口:
java
/**
* 演示消息转换器如何把请求体转成对象,再把返回对象转成 JSON。
*
* @param request 创建用户请求体
* @return 创建后的用户视图对象
*/
@PostMapping("/users")
public UserVO create(@RequestBody CreateUserRequest request) {
return new UserVO(1L, request.getName());
}它背后实际做了两件事:
- 把请求体 JSON 转成
CreateUserRequest - 再把
UserVO转成 JSON 响应
10. 校验和异常处理为什么也属于 MVC 主线
因为一次请求真正走完,不只是“进入 Controller 再返回”这么简单。
中间还包括:
- 参数校验
- 类型转换
- 异常处理
- 响应统一格式
10.1 参数校验
例如常见的:
@Valid@Validated
它们解决的是:请求参数在进入业务逻辑之前,先做结构和规则校验。
例如:
java
/**
* 创建用户接口的请求体对象。
*/
public class CreateUserRequest {
@NotBlank
private String name;
@Min(1)
private Integer age;
// getter/setter
}
/**
* 演示 `@RequestBody` 会把 JSON 请求体自动绑定成 Java 对象。
*
* @param request 创建用户请求体
* @return 创建后的用户视图对象
*/
@PostMapping("/users")
public UserVO create(@Valid @RequestBody CreateUserRequest request) {
return new UserVO(1L, request.getName());
}10.2 统一异常处理
例如常见的:
@ExceptionHandler@ControllerAdvice@RestControllerAdvice
它们解决的是:不要让每个 Controller 自己重复写异常兜底逻辑。
例如:
java
/**
* 统一处理控制层抛出的业务异常。
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理参数非法异常,并返回 400 响应。
*
* @param ex 当前捕获到的非法参数异常
* @return 带有错误信息的 `400 Bad Request` 响应
*/
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
}11. Spring MVC 和 Spring Boot 是什么关系
这也是非常常见的混淆点。
Spring MVC是 Web 框架Spring Boot是更高层的工程化体系
Spring Boot 经常把 Spring MVC 用更省心的方式自动配置起来,但它们不是一回事。
例如你写:
@RestController@GetMapping- 自动返回 JSON
这些底层大多还是在用 Spring MVC 的能力,只是 Boot 帮你把环境搭好了。
12. 工程上最常见的几个误区
12.1 误区一:Spring MVC 就是写 Controller
Controller 只是入口层的一部分。
真正的主线还包括:
- 请求分发
- 参数绑定
- 消息转换
- 异常处理
- 响应输出
12.2 误区二:前后端分离以后,MVC 就没意义了
不是。
只是 View 的角色变轻了,Spring MVC 仍然在负责请求分发和响应组织。
12.3 误区三:返回 JSON 是 Spring Boot 的能力
这是 Spring MVC 的 Web 层处理能力,Boot 更多是在自动配置它。
13. 一句话总结
Spring MVC 的核心价值,是把 HTTP 请求从接入、分发、参数绑定、方法调用到响应输出的整条链路统一组织起来,让 Java Web 开发从零散 Servlet 编程,变成结构清晰的控制层模型。