一、基本定义
Spring MVC(全称 Spring Web MVC)是 Spring 框架原生提供的Web 层应用开发框架,基于经典的 MVC 设计模式实现,是 Java 企业级开发中最主流的 Web 开发方案之一,主要用于构建传统 Web 页面应用与 RESTful 接口服务。
它建立在 Servlet API 之上,对原生 Servlet 开发的繁琐流程做了深度封装,让开发者只需关注业务逻辑编写,无需手动处理参数解析、响应封装、路径映射等重复工作。
二、MVC 设计模式与 Spring MVC 的对应
MVC 是一种软件架构设计思想,将 Web 应用按职责拆分为三层,实现关注点分离:
- Model(模型层):负责数据封装与核心业务逻辑,对应 Java 实体类、Service 业务层、DAO 数据层
- View(视图层):负责数据展示与页面渲染,对应 JSP、Thymeleaf 模板页、前端页面
- Controller(控制层):负责接收请求、调用模型、返回视图 / 数据,对应 Spring MVC 中的 Controller 组件
在当前前后端分离的主流场景下,View 层由前端独立实现,Spring MVC 通常只负责提供数据接口(返回 JSON),此时 Controller 直接返回业务数据对象,由框架自动序列化为 JSON 响应。
三、核心工作流程(请求全链路)
Spring MVC 的核心中枢是DispatcherServlet(前端控制器),所有 HTTP 请求都会先经过它,再由它统一调度各个组件完成处理。完整请求处理流程如下:
- 用户发起 HTTP 请求,请求被
DispatcherServlet统一接收 DispatcherServlet调用HandlerMapping,根据请求 URL 匹配对应的 Controller 方法,并组装拦截器链DispatcherServlet调用HandlerAdapter,由适配器适配并执行目标 Controller 方法- Controller 执行业务逻辑,返回处理结果(视图名、ModelAndView、JSON 对象等)
HandlerAdapter将执行结果返回给DispatcherServlet- 若返回视图名,
DispatcherServlet调用ViewResolver(视图解析器),将逻辑视图名解析为具体的 View 对象 DispatcherServlet调用 View 对象进行视图渲染,将 Model 数据填充到视图中- 最终
DispatcherServlet将渲染结果或 JSON 数据作为 HTTP 响应返回给用户
四、核心组件说明
表格
| 组件 | 核心作用 |
|---|---|
| DispatcherServlet | 前端控制器,整个框架的调度中枢,统一接收请求并分发,降低各组件耦合 |
| HandlerMapping | 处理器映射器,建立请求 URL 与 Controller 方法的映射关系,同时组装拦截器链 |
| HandlerAdapter | 处理器适配器,采用适配器模式统一调用不同类型的 Controller,屏蔽调用方式的差异 |
| Handler / Controller | 业务处理器,由开发者编写,负责接收参数、调用业务层、返回处理结果 |
| ViewResolver | 视图解析器,将逻辑视图名(如"login")解析为具体的视图对象(如 JSP、Thymeleaf 页面) |
| View | 视图,负责数据渲染,将 Model 数据填充后生成最终的 HTML、JSON、Excel 等响应内容 |
| HandlerInterceptor | 拦截器,在请求处理的三个节点(Controller 执行前、执行后、请求结束后)插入自定义逻辑 |
五、核心优势与特性
Spring 生态无缝集成天然支持 Spring 的依赖注入(DI)、AOP、事务管理等能力,Spring 容器中的 Bean 可直接在 Controller 中注入使用。
注解驱动开发通过
@Controller、@RequestMapping、@RequestParam、@ResponseBody等注解,极大简化配置,替代了传统 Servlet 繁琐的 web.xml 配置。强大的参数绑定与转换自动将请求参数(查询参数、表单、JSON 请求体)封装为 Java 对象,支持类型自动转换、数据校验、日期格式化等能力。
高度可扩展支持自定义拦截器、参数解析器、消息转换器、视图解析器、异常处理器,可灵活适配各类业务需求。
RESTful 友好原生支持 REST 风格接口开发,通过
@RestController、路径变量、HTTP 方法语义化(GET/POST/PUT/DELETE)快速构建标准化接口。多视图兼容支持 JSP、Thymeleaf、Freemarker、Excel、PDF 等多种视图输出形式,满足不同业务场景。
六、补充:与 Servlet 过滤器的执行顺序
上一问提到的拦截器是 Spring MVC 的内部组件,而过滤器(Filter)是 Servlet 官方规范的组件,二者执行顺序为:Servlet 过滤器 → DispatcherServlet → Spring MVC 拦截器 → Controller
过滤器作用于请求进入 Servlet 容器的阶段,拦截器作用于 Spring MVC 内部的请求生命周期。
在 Spring Boot 环境中,Spring MVC 相关组件会被自动配置,开发者只需引入spring-boot-starter-web依赖即可直接使用,无需手动配置 DispatcherServlet 和基础视图解析器。
Spring MVC 常用注解详解(附完整示例)
以下按使用频率从高到低分类讲解,全部基于 Spring Boot 环境,覆盖日常开发 95% 以上的场景。
一、控制器类注解(类级别)
1. @Controller
- 作用:标记一个类为 Spring MVC 控制器,负责接收并处理 HTTP 请求,传统页面开发中用于返回逻辑视图名。
- 位置:类上方
- 特点:默认方法返回值会被当做视图名,走视图解析器渲染页面;若要返回 JSON 数据,需要在方法上额外加
@ResponseBody。
@Controller @RequestMapping("/page") public class PageController { // 返回 login 视图名,视图解析器会匹配到 login.html / login.jsp @GetMapping("/login") public String loginPage() { return "login"; } }2. @RestController
- 作用:复合注解 =
@Controller+@ResponseBody,是前后端分离项目的首选。 - 位置:类上方
- 特点:类中所有方法默认直接返回数据(JSON/XML),不会走视图解析器,无需每个方法单独加
@ResponseBody。
@RestController @RequestMapping("/api/user") public class UserController { // 直接返回 JSON 格式的字符串/对象 @GetMapping("/hello") public String hello() { return "hello spring mvc"; } }二、请求映射注解(类 + 方法级别)
用于建立「请求 URL + 请求方式」与「处理方法」的映射关系。
1. @RequestMapping
- 作用:通用请求映射注解,可标注在类和方法上;类上定义父路径,方法上定义子路径。
- 核心属性:
value / path:请求路径,支持数组配置多个路径method:指定请求方式(RequestMethod.GET/POST/PUT/DELETE等)params:限定请求必须包含 / 不包含指定参数consumes:限定请求体的媒体类型(如application/json)produces:限定响应的媒体类型
@RestController @RequestMapping("/api/user") // 类级别的父路径 public class UserController { // 匹配 GET /api/user/list?type=admin @RequestMapping(value = "/list", method = RequestMethod.GET, params = "type=admin") public String list() { return "管理员用户列表"; } }2. REST 风格派生注解
等价于指定了method的@RequestMapping,是目前接口开发的主流写法:
| 注解 | 对应请求方式 | 典型用途 |
|---|---|---|
@GetMapping | GET | 查询数据 |
@PostMapping | POST | 新增数据 |
@PutMapping | PUT | 修改全量数据 |
@DeleteMapping | DELETE | 删除数据 |
@PatchMapping | PATCH | 修改部分数据 |
@RestController @RequestMapping("/api/user") public class UserController { @GetMapping("/{id}") // GET 查询单个用户 public String getById(@PathVariable Long id) { return "查询用户:" + id; } @PostMapping // POST 新增用户 public String addUser() { return "新增用户"; } @PutMapping // PUT 修改用户 public String updateUser() { return "修改用户"; } @DeleteMapping("/{id}") // DELETE 删除用户 public String deleteUser(@PathVariable Long id) { return "删除用户:" + id; } }三、请求参数绑定注解(方法参数级别)
这是开发中最核心的一类注解,用于把 HTTP 请求中的数据自动注入到方法参数中。
1. @RequestParam
- 作用:获取URL 查询参数、表单参数(
application/x-www-form-urlencoded) - 核心属性:
name / value:对应请求中的参数名required:是否必填,默认truedefaultValue:参数未传时的默认值
@GetMapping("/page") // 请求示例:GET /api/user/page?pageNum=1&pageSize=20 public String pageList( @RequestParam("pageNum") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize // 不传默认10 ) { return "第" + pageNum + "页,每页" + pageSize + "条"; }2. @PathVariable
- 作用:获取 URL 路径中的变量,是RESTful 风格接口的核心注解。
- 场景:把参数放在路径中,如
/user/100中的100
@GetMapping("/{id}/{name}") // 请求示例:GET /api/user/100/zhangsan public String getInfo( @PathVariable Long id, @PathVariable String name ) { return "id:" + id + ", name:" + name; }3. @RequestBody
- 作用:接收JSON 格式的请求体,自动将 JSON 反序列化为 Java 对象。
- 场景:POST/PUT 请求提交复杂对象数据,前后端分离最常用。
- 注意:一个方法中最多只能有一个
@RequestBody;GET 请求没有请求体,不能使用。
先定义实体类:
public class User { private Long id; private String username; private Integer age; // getter、setter、无参构造省略 }使用示例:
@PostMapping("/add") // 请求体:{"id":1,"username":"张三","age":20} public String addUser(@RequestBody User user) { return "新增用户:" + user.getUsername(); }4. @RequestHeader
- 作用:获取指定请求头的值
@GetMapping("/header") public String getHeader(@RequestHeader("token") String token) { return "请求头中的token:" + token; }5. @CookieValue
- 作用:获取 Cookie 中的指定值
@GetMapping("/cookie") public String getCookie(@CookieValue("JSESSIONID") String sessionId) { return "SessionID:" + sessionId; }四、响应与状态注解
1. @ResponseBody
- 作用:将方法返回值直接作为 HTTP 响应体输出,自动通过消息转换器序列化为 JSON。
- 位置:方法上 / 类上
- 说明:
@RestController已经默认包含此注解,仅在@Controller类中需要返回 JSON 时单独使用。
@Controller public class TestController { @ResponseBody @GetMapping("/json") public User jsonTest() { return new User(1L, "张三", 20); // 直接返回 JSON 对象 } }2. @ResponseStatus
- 作用:自定义响应的 HTTP 状态码和提示信息
- 位置:方法上 / 异常类上
@PostMapping("/create") @ResponseStatus(code = HttpStatus.CREATED, reason = "创建成功") public void create() { // 业务逻辑,响应状态码为 201 }五、全局增强与异常处理注解
1. @ControllerAdvice + @RestControllerAdvice
- 作用:控制器全局增强组件,用于实现全局异常处理、全局数据绑定、全局参数预处理。
@RestControllerAdvice=@ControllerAdvice+@ResponseBody,统一返回 JSON 格式的异常结果。
2. @ExceptionHandler
- 作用:捕获指定类型的异常,进行统一处理
- 可写在 Controller 内做局部异常处理,也可配合
@RestControllerAdvice做全局异常处理。
全局异常处理完整示例:
@RestControllerAdvice public class GlobalExceptionHandler { // 捕获参数非法异常 @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String handleIllegalArg(IllegalArgumentException e) { return "参数错误:" + e.getMessage(); } // 捕获所有未处理的系统异常 @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String handleException(Exception e) { return "系统异常,请联系管理员"; } }六、综合实战完整示例
1. 实体类 User.java
public class User { private Long id; private String username; private Integer age; public User() {} public User(Long id, String username, Integer age) { this.id = id; this.username = username; this.age = age; } // getter、setter 省略 }2. 用户控制器 UserController.java
import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/users") public class UserController { // 1. 路径变量 + GET 查询单个用户 @GetMapping("/{id}") public User getUserById(@PathVariable Long id) { return new User(id, "用户" + id, 20); } // 2. 查询参数 + 分页列表 @GetMapping("/list") public String listUsers( @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize ) { return "分页查询:第" + pageNum + "页,每页" + pageSize + "条"; } // 3. JSON请求体 + 新增用户 @PostMapping public String addUser(@RequestBody User user) { return "新增成功:" + user.getUsername(); } // 4. 路径变量 + JSON请求体 + 修改用户 @PutMapping("/{id}") public String updateUser(@PathVariable Long id, @RequestBody User user) { return "修改ID为" + id + "的用户:" + user.getUsername(); } // 5. 路径变量 + 删除用户 @DeleteMapping("/{id}") public String deleteUser(@PathVariable Long id) { return "删除用户ID:" + id; } }3. 全局异常处理 GlobalExceptionHandler.java
import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String paramError(IllegalArgumentException e) { return "请求参数非法:" + e.getMessage(); } @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public String systemError() { return "服务器内部错误,请稍后重试"; } }七、常见易错点
- GET 请求不能用
@RequestBody:GET 协议没有请求体,传参只能用@RequestParam或@PathVariable。 - 一个方法只能有一个
@RequestBody:请求体只有一个,无法拆分注入多个对象。 @RestController不会走视图解析器:如果需要返回页面,必须用@Controller。@PathVariable可选参数:若要让路径变量可选,需配置required = false,同时路径要支持空匹配。