整体流程:
SpringMVC 六大核心组件
SpringMVC 所有功能都靠这 6 个组件配合,只有 Controller 是我们写的,其余都是框架自带:
- DispatcherServlet(前端控制器) 核心大脑 / 总调度 所有浏览器请求,第一个到达的就是它,统一分配任务,不做具体业务。
- HandlerMapping(处理器映射器) 找路工具 根据请求的 URL,找到对应的
Controller处理方法。 - HandlerAdapter(处理器适配器) 执行工具统一调用我们写的 Controller 方法(适配不同写法的 Controller)。
- Handler/Controller(处理器 / 控制器) 业务核心 我们自己编写:接收参数、调用 Service、处理业务、返回数据 / 视图。
- ViewResolver(视图解析器) 找页面工具 把逻辑视图名(如
index)转换成真实的页面路径(如/WEB-INF/index.jsp)。 - View(视图) 渲染工具把数据填充到页面,返回给浏览器展示。
SpringMVC 完整执行流程
这是 SpringMVC 的工作原理,一步一步通俗讲解:
浏览器 → 请求 → DispatcherServlet(总控)
↓
1. 总控 询问 HandlerMapping:这个URL对应哪个Controller?
2. 映射器 返回 对应Controller的方法
↓
3. 总控 命令 HandlerAdapter:去执行这个方法!
4. 适配器 调用 我们写的Controller(处理业务)
5. Controller 返回 ModelAndView(数据 + 视图名)给适配器
↓
6. 适配器 把结果还给 总控
↓
7. 总控 交给 ViewResolver:解析视图名,找到真实页面
8. 总控 交给 View:把数据渲染到页面上
↓
9. 渲染后的页面 → 响应给浏览器
一句话总结 :DispatcherServlet 统一调度,映射器找方法,适配器执行业务,解析器找页面,视图渲染结果。
体验:
注解类:
@Configuration
@ComponentScan("com.atguigu.controller")
public class MvcConfig {
@Bean
public RequestMappingHandlerMapping handlerMapping(){
return new RequestMappingHandlerMapping();
}
@Bean
public RequestMappingHandlerAdapter handlerAdapter(){
return new RequestMappingHandlerAdapter();
}
}
public class SpringMvcInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MvcConfig.class};//在这里面写要加载的配置文件,因为这个方法的父类的父类已经创建了ioc容器,所以这里直接写要加载的配置文件就可以了
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
controller层:
@Controller
public class HelloController {
@RequestMapping("springmvc/hello")//对外访问的地址 到handlerMapping注册的解析
@ResponseBody//直接返回字符串给前端,不要找视图解析器
public String hello(){
System.out.println("hello");
return "springmvc/hello";
}
}
路径设置注解:
@RequestMapping

pringMVC 中设置请求路径的核心注解只有两个体系:
- 通用注解 :
@RequestMapping(最基础、功能最全) - 简化注解 :
@GetMapping/@PostMapping/@PutMapping/@DeleteMapping/@PatchMapping(专用、语义化)
作用:将浏览器的 URL 请求,绑定到 Controller 的类 / 方法上,是 Web 开发的「入口注解」,必须彻底吃透!
一、核心总览
|-------------------|-------------------|---------------------|
| 注解 | 作用 | 适用场景 |
| @RequestMapping | 通用路径映射,支持所有请求方法 | 类上定义统一前缀、需要灵活配置请求方式 |
| @GetMapping | 仅绑定 GET 请求 | 查询数据、页面跳转 |
| @PostMapping | 仅绑定 POST 请求 | 提交表单、新增数据 |
| @PutMapping | 仅绑定 PUT 请求 | 全量修改数据 |
| @DeleteMapping | 仅绑定 DELETE 请求 | 删除数据 |
| @PatchMapping | 仅绑定 PATCH 请求 | 局部修改数据 |
二、基础用法(最常用)
1. 注解位置:类 + 方法 组合使用
-
类上 :定义统一路径前缀(模块化、避免重复)
-
方法上 :定义具体接口路径
@Controller
// 类前缀:所有方法路径都自动拼接 /user
@RequestMapping("/user")
public class UserController {// 最终访问路径:/user/list @RequestMapping("/list") public String userList() { return "userList"; } // 最终访问路径:/user/add @RequestMapping("/add") public String addUser() { return "addUser"; }}
三、@RequestMapping 全属性详解
@RequestMapping 是万能注解,支持 6 个核心属性,精准控制请求匹配规则:
1. value / path(路径,等价)
-
唯一必写属性,指定请求 URL
-
支持字符串数组:一个方法绑定多个路径
// 访问 /test1 或 /test2 都能执行该方法
@RequestMapping(value = {"/test1", "/test2"})
public void test() {}
2. method(限制请求方法)
指定仅允许的 HTTP 请求方式(GET/POST/PUT/DELETE)
// 仅允许 POST 请求访问
@RequestMapping(value = "/save", method = RequestMethod.POST)
public void save() {}
3. params(限制请求参数)
要求请求必须携带 / 不携带指定参数,否则 404 报错
// 必须携带 id 参数
@RequestMapping(value = "/get", params = "id")
// 必须携带 id=1 参数
@RequestMapping(value = "/get", params = "id=1")
// 不能携带 name 参数
@RequestMapping(value = "/get", params = "!name")
4. headers(限制请求头)
要求请求头必须满足指定条件(常用于接口鉴权、格式限制)
// 请求头必须包含 Content-Type=application/json
@RequestMapping(value = "/json", headers = "Content-Type=application/json")
5. consumes(限制请求数据类型)
指定接收的请求体格式(前端传参格式)
// 仅接收 JSON 格式请求
@RequestMapping(value = "/add", consumes = "application/json")
6. produces(限制响应数据类型)
指定返回给前端的数据格式
// 返回 JSON 格式数据
@RequestMapping(value = "/get", produces = "application/json;charset=utf-8")
简化版注解
Spring 4.3 推出专用请求注解 ,替代 @RequestMapping(method=xxx),代码更简洁、可读性更高:
等价关系
// 原始写法
@RequestMapping(value = "/list", method = RequestMethod.GET)
// 简化写法(完全等价)
@GetMapping("/list")
完整示例(RESTful 标准接口)
@RestController
@RequestMapping("/user")
public class UserController {
// 查询:GET
@GetMapping("/{id}")
public User getById(@PathVariable Integer id) {}
// 新增:POST
@PostMapping
public void add(@RequestBody User user) {}
// 修改:PUT
@PutMapping("/{id}")
public void update(@PathVariable Integer id, @RequestBody User user) {}
// 删除:DELETE
@DeleteMapping("/{id}")
public void delete(@PathVariable Integer id) {}
}
Ant 风格通配符路径
支持 3 种通配符,适配模糊路径,非常实用:
|---------|------------------|------------|---------------|-------------|
| 通配符 | 含义 | 示例 | 匹配 | 不匹配 |
| ? | 匹配单个字符 | /user/? | /user/1 | /user/10 |
| * | 匹配任意字符(单层路径) | /user/* | /user/list | /user/a/b |
| ** | 匹配任意多层路径 | /user/** | /user/a/b/c | 无 |
// 匹配 /user/info、/user/abc、/user/123
@GetMapping("/user/*")
// 匹配 /user/list、/user/a/list、/user/a/b/list
@GetMapping("/user/**/list")
RESTful 路径变量
配合 {变量名} 定义动态路径 ,用 @PathVariable 绑定参数:
1. 基础用法
// 路径:/user/100 → id=100
@GetMapping("/user/{id}")
public User getById(@PathVariable Integer id) {
return userService.getById(id);
}
2. 多个路径变量
// 路径:/user/100/order/200 → userId=100, orderId=200
@GetMapping("/user/{userId}/order/{orderId}")
public Order getOrder(@PathVariable Integer userId, @PathVariable Integer orderId) {}
3. 路径变量别名
// 路径变量名和参数名不一致时
@GetMapping("/user/{uid}")
public User getById(@PathVariable("uid") Integer id) {}
路径匹配优先级
当多个路径匹配同一个请求时,SpringMVC 按精确优先规则匹配:
- 精确匹配 > 通配符匹配
- 通配符优先级:
?>*>**
示例:
- 请求
/user/1 - 匹配顺序:
/user/1(精确)>/user/?>/user/*>/user/**
关键注意事项
1. 斜杠 / 可加可不加
Spring 自动处理,以下写法完全等价:
@RequestMapping("user")
@RequestMapping("/user")
2. 禁止重复路径
同一个项目中,相同路径 + 相同请求方法 会直接启动报错!
3. 类上注解不写 method
类上仅定义前缀,不要写 method,否则所有方法都会被限制请求方式。
4. 静态资源不冲突
路径注解仅拦截 Controller 请求,静态资源(css/js/img)需单独配置放行。'
区别param和json:
@RequestParam (普通 Param 参数) VS JSON (@RequestBody)
核心一句话先记住:Param 是键值对表单参数;JSON 是请求体结构化对象参数,靠请求头 Content-Type****区分。
分水岭:Content-Type(
1. Param 参数(@RequestParam)
前端请求头固定:
Content-Type: application/x-www-form-urlencoded
这是浏览器表单默认格式。
2. JSON 参数(@RequestBody)
前端请求头必须手动设置:
Content-Type: application/json
前后端分离、Vue/React 项目专用。
详细对比
|------------|------------------------------|-------------------|
| 对比维度 | Param 键值对参数 | JSON 对象参数 |
| 传参位置 | URL 地址后 / 表单请求体 | 纯请求 Body 内部 |
| 后端注解 | @RequestParam | @RequestBody |
| 数据格式 | key=value&key2=value2 扁平键值 | {json键值对象} 结构化 |
| 复杂嵌套 | 不支持嵌套对象 | 完美支持多级嵌套、数组 |
| GET 请求 | 完全支持 | GET 无请求体,禁止使用 |
| 编码处理 | 自动 URL 编码转义 | 原生 JSON 格式,不自动编码 |
Param 参数 完整演示
前端传参形式
方式 1:URL 拼接
http://localhost/user/login?username=zhangsan&password=123
方式 2:form 表单提交(默认格式)
后端接收写法
// 简单参数接收
@PostMapping("/login")
public String login(
@RequestParam String username,
@RequestParam String password
){
System.out.println(username + password);
return "success";
}
// 也可以直接封装简单实体类(扁平属性)
@PostMapping("/login")
public String login(User user){}
特点:属性必须扁平,不能嵌套复杂对象。
四、2. JSON 参数 完整演示
前端传参形式(Vue/Axios)
请求头:Content-Type: application/json请求体 raw JSON:
{
"username":"zhangsan",
"password":"123",
"address":{
"city":"杭州"
}
}
后端接收写法
@PostMapping("/login")
// 必须加 @RequestBody
public String login(@RequestBody User user){
System.out.println(user.getUsername());
return "success";
}
特点:支持嵌套对象、数组、复杂结构,前后端分离标配。
五、高频易错坑
坑 1:GET 请求不能用 @RequestBody
HTTP 规范:GET 没有请求体 GET 只能用:@RequestParam GET 写 @RequestBody 直接报错
坑 2:注解不能混用
- 表单 param:不能加
@RequestBody - JSON 请求:必须加
@RequestBody,不加接收不到数据
坑 3:JSON 必须严格匹配字段名
JSON 键名 和 Java 实体类属性名 大小写完全一致,否则为 null;Param 参数匹配宽松,只要 key 同名即可。
坑 4:文件上传不属于这两类
文件上传 Content-Type 是 multipart/form-data,单独用MultipartFile接收。
param参数接收参数:
最基础:单个基本参数接收
1. 无注解直接接收
参数名 = 请求的 key 名,自动匹配赋值
@GetMapping("/test1")
public String test1(String username, Integer age) {
System.out.println(username); // 接收 ?username=张三
System.out.println(age); // 接收 ?age=20
return "success";
}
✅ 前端传参:http://localhost:8080/test1?username=张三&age=20
2. 带 @RequestParam 注解
手动绑定参数,可控性更强
@PostMapping("/test2")
public String test2(
@RequestParam String username,
@RequestParam Integer age
) {
return "success";
}
二、@RequestParam 三大核心属性
解决参数名不一致、必传、默认值问题
@RequestParam(
value = "name", // 前端传的 key 名
required = true, // 是否必传(默认 true,不传报错 400)
defaultValue = "游客" // 不传时的默认值
)
实战示例
@GetMapping("/user")
public String getUser(
// 前端传 name,后端用 username 接收
@RequestParam(value = "name") String username,
// 非必传,默认 18
@RequestParam(required = false, defaultValue = "18") Integer age
) {
return "success";
}
前端传参:/user?name=张三 → age 自动 = 18
三、进阶:数组 / 集合参数接收
1. 数组接收
前端传同名多个参数
@GetMapping("/hobby")
public String hobby(@RequestParam String[] hobby) {
// 接收 ?hobby=篮球&hobby=足球&hobby=游戏
return "success";
}
2. List 集合接收(必须加 @RequestParam)
不加注解会报错! Spring 无法直接封装 List
@GetMapping("/hobbyList")
public String hobbyList(@RequestParam List<String> hobby) {
return "success";
}
四、最常用:POJO 实体类自动封装
前端传多个参数,Spring 自动封装成 Java 对象,不用一个个接收!
1. 实体类
public class User {
private String username;
private Integer age;
// getter/setter
}
2. 后端接收(零注解,直接写实体类)
@PostMapping("/addUser")
public String addUser(User user) { // 自动封装所有参数
System.out.println(user.getUsername());
System.out.println(user.getAge());
return "success";
}
前端传参:?username=张三&age=20 → 自动封装到 User 对象
五、高级:嵌套 POJO 接收
Param 也支持嵌套对象 ,前端传参格式:对象.属性
1. 嵌套实体
public class User {
private String username;
private Address address; // 嵌套对象
}
public class Address {
private String city;
}
2. 后端接收
@PostMapping("/addUserAddr")
public String addUserAddr(User user) {
System.out.println(user.getAddress().getCity());
return "success";
}
前端传参:?username=张三&address.city=北京
关键区别:不加注解 vs 加 @RequestParam
|---------------------------------|----------|---------------|---------------|
| 写法 | 自动匹配 | 参数必传 | 参数名不匹配 |
| String username | ✅ 是 | ❌ 非必传(为 null) | ❌ 无法接收 |
| @RequestParam String username | ✅ 是 | ✅ 必传(默认) | ✅ 可用 value 指定 |
总结
- 简单参数、非必传 → 不加注解
- 必传、改名、默认值 → 必须加 @RequestParam
高频避坑
- GET/POST 都能用 ParamGET 放 URL 后,POST 放请求体,格式完全一样。
- Param 不能接收 JSON! JSON 必须用
@RequestBody,Param 只认 key=value。 - 基本类型不能为 null
int age必传,否则报错;用Integer age可空。 - 中文乱码问题POST 请求中文乱码 → 配置 Spring 编码过滤器。
- List 必须加 @RequestParam数组不用,List 必须加!
路径传参:

先分清:路径参数 和 普通 Param 参数
1. 普通 Param 参数
地址:/user?**id=100&name=张三**特点:问号拼接,键值对,用@RequestParam接收
2. 路径参数(REST 专用)
地址:/user/**100/张三**特点:直接嵌在 URL 路径里,无问号 ,用{变量名}占位 + @PathVariable接收核心注解:@PathVariable
二、最基础:单个路径参数接收
1. 写法模板
- 映射路径写占位符:
/{参数名} - 形参添加:
@PathVariable
2. 完整代码示例
访问地址:http://localhost/user/20
@Controller
@RequestMapping("/user")
public class UserController {
// {id} 是路径占位符
@GetMapping("/{id}")
public String getUser(
// 绑定路径里的 id
@PathVariable Integer id
){
System.out.println("路径参数id:" + id); // 输出20
return "success";
}
}
规则:占位符名字 和 形参名字一致,直接绑定
三、变量名不一致:别名绑定
路径占位名 和 Java 参数名不一样时,手动指定映射:访问:/user/105
// 路径占位:uid
@GetMapping("/{uid}")
public String getById(
// 绑定 uid 给 id变量
@PathVariable("uid") Integer id
){
System.out.println(id); //105
return "success";
}
四、多个路径参数
路径里可以放多个{}占位符:访问地址:/order/10/2026
@GetMapping("/order/{userId}/{year}")
public String getOrder(
@PathVariable Integer userId,
@PathVariable Integer year
){
System.out.println(userId + " " + year);
return "success";
}
五、@PathVariable 三个核心属性
@PathVariable(
value = "uid", // 绑定路径占位名
required = true, // 是否必填(默认true,不填404)
name = "uid" // 和value完全等价
)
示例(非必填):
@GetMapping("/info/{name}")
public String info(
@PathVariable(required = false) String name
){
return "success";
}
六、进阶:正则限制路径参数格式
防止非法参数,给占位符加正则约束:语法:{变量名:正则表达式}
// id必须是纯数字
@GetMapping("/detail/{id:\\d+}")
public String detail(@PathVariable Integer id){
return "success";
}
- 访问
/detail/99✅ 正常匹配 - 访问
/detail/abc❌ 404 匹配失败
常用正则:
\\d+:只能数字[a-zA-Z0-9]+:字母 + 数字
七、路径参数直接封装实体类
SpringMVC 自动把路径参数注入 POJO 属性:实体类:
public class User {
private Integer id;
private String username;
// get/set
}
接口:访问:/user/88/lisi
@GetMapping("/user/{id}/{username}")
public String getUser(User user){
System.out.println(user.getId()); //88
System.out.println(user.getUsername()); //lisi
return "success";
}
不用逐个注解,自动匹配属性名!
高频避坑
坑 1:GET/POST 都能用路径参数
REST 风格里:
- 查询 GET + 路径参数
- 删除 DELETE + 路径参数完全通用
坑 2:路径参数不能省略(默认必填)
required=true,少一段路径直接 404
坑 3:不要和普通 Param 搞混
/user?name=xxx→@RequestParam/user/xxx→@PathVariable
坑 4:中文路径参数乱码
Tomcat 默认编码问题,配置server.xml URIEncoding="UTF-8" 解决
坑 5:不能搭配 @RequestBody
路径参数是 URL 里的,JSON 请求体才用@RequestBody,两者互不干扰,可以共存但不要混用理解
对比
|-------------|--------------|-----------------|----------------------|
| 类型 | URL 样式 | 注解 | 使用场景 |
| 普通 Param 参数 | /user?id=1 | @RequestParam | 传统表单、查询筛选 |
| 路径参数 | /user/1 | @PathVariable | RESTful 接口、主键查询 / 删除 |
json传参:
核心定调
JSON 传参 = 前端传 JSON 格式字符串 → 放在 HTTP 请求体中 → 后端用 @RequestBody****接收 ✅ 专用场景:Vue/React/ 小程序等前后端分离项目✅ 唯一限制:GET 请求不能用(HTTP 规范中 GET 无请求体)
二、JSON 传参 3 个核心前提
-
传参位置 :HTTP 请求体(Body) 中,不在 URL、不在表单
-
请求头:必须设置
Content-Type: application/json; charset=utf-8
-
后端注解 :@RequestBody(SpringMVC 接收 JSON 的唯一注解)
三、环境依赖(零配置)
- 纯 SpringMVC :自动依赖
jackson-databind(JSON 转换工具) - SpringBoot :引入
spring-boot-starter-web后完全自动配置,不用管任何配置
四、基础用法(4 种最常用场景)
场景 1:接收普通实体对象(开发 90% 的用法)
前端传参(JSON 格式)
json
{
"username": "张三",
"age": 20,
"email": "123@qq.com"
}
后端代码
-
实体类
public class User {
// JSON键名 必须 = 实体类属性名(大小写一致)
private String username;
private Integer age;
private String email;
// getter/setter/toString
} -
Controller
@RestController
@RequestMapping("/user")
public class UserController {
/**
* @RequestBody 作用:
* 1. 读取请求体中的JSON字符串
* 2. 自动转换为Java User对象
*/
@PostMapping("/add")
public String addUser(@RequestBody User user) {
System.out.println(user);
return "新增成功";
}
}
场景 2:接收 Map 集合(无需建实体类,灵活)
适合临时接口、参数不固定的场景
@PostMapping("/test")
public String testMap(@RequestBody Map<String, Object> map) {
System.out.println(map.get("username"));
System.out.println(map.get("age"));
return "测试成功";
}
场景 3:接收数组 / List 集合(批量数据)
前端传 JSON 数组
[1,2,3,4,5]
后端接收
@PostMapping("/batch")
public String batchDelete(@RequestBody List<Integer> ids) {
System.out.println(ids); // [1,2,3,4,5]
return "批量删除成功";
}
场景 4:接收嵌套 JSON 对象(复杂数据)
前端嵌套 JSON
{
"username": "张三",
"address": {
"city": "北京",
"detail": "朝阳区"
}
}
后端嵌套实体
// 地址实体
public class Address {
private String city;
private String detail;
}
// 用户实体
public class User {
private String username;
private Address address; // 嵌套对象
}
// 直接接收,自动封装嵌套数据
@PostMapping("/save")
public String save(@RequestBody User user) {
return user.getAddress().getCity(); // 北京
}
高级用法:混合传参(JSON + 路径参数)
RESTful 接口最常用组合:路径参数定位资源,JSON 传修改数据
@PutMapping("/user/{id}")
public String update(
// 路径参数:定位用户
@PathVariable Integer id,
// JSON参数:用户新数据
@RequestBody User user
) {
System.out.println("修改用户id:" + id);
System.out.println("新数据:" + user);
return "修改成功";
}
@RequestBody 核心规则(避坑关键)
- 必填性 默认
required = true,前端必须传 JSON 请求体 ,否则报 400 错误;非必填:@RequestBody(required = false) - 唯一限制 一个 Controller 方法最多只能有 1 个 @RequestBody(请求体只有一个)
- 自动转换 框架通过
消息转换器自动完成:JSON字符串 ↔ Java对象 - 字段匹配 JSON 键名 ≠ Java 属性名 → 赋值为
null(必须严格一致)
踩坑
坑 1:GET 请求用 @RequestBody → 直接报错
@GetMapping + @RequestBody
正确:JSON 传参只能用 POST/PUT/PATCH/DELETE
坑 2:忘记加 @RequestBody → 接收不到数据
前端传 JSON,后端不加注解,所有属性都是null
坑 3:前端请求头错误
前端没设置 Content-Type: application/json,后端无法解析 JSON
坑 4:字段名大小写 / 拼写错误
JSON 写 userName,后端写 username → 接收为 null
坑 5:直接用基本类型接收
❌ 错误:@RequestBody String username✅ 正确:基本类型用Param / 路径参数 ,JSON 用对象 / Map / 集合
八、对比
|-------------|-----------------|------------------|----------|----------|----------------|
| 传参类型 | URL 样式 | 核心注解 | 传参位置 | 支持请求 | 适用场景 |
| 普通 Param | /user?name=张三 | @RequestParam | URL / 表单 | GET/POST | 简单参数、表单 |
| 路径参数 | /user/100 | @PathVariable | URL 路径 | 全部 | RESTful 定位资源 |
| JSON 传参 | /user | @RequestBody | 请求体 | POST/PUT | 前后端分离、复杂对象 |
九、总结
- JSON 传参靠请求体 ,必须配
application/json请求头; - 后端唯一注解 @RequestBody,自动转 Java 对象;
- 支持对象、嵌套、集合、Map,复杂数据首选;
- GET 不能用,一个方法只能用一个;
- 和 Param、路径参数可以混合使用,互不冲突。
@EnableWebMvc 内部通过 @Import 导入了:WebMvcConfigurationSupport这个类会自动帮你创建 SpringMVC 所有核心组件:
- Handler 映射器、适配器
- JSON 消息转换器(Jackson)👉支持
@RequestBody - 日期格式化器
- 参数校验器
- 默认视图规则
- 基础 MVC 适配全套配置
三、什么时候必须加?
场景 1:纯 SSM 无 XML 全注解开发
@Configuration
@ComponentScan("com.controller")
@EnableWebMvc // 必加!!
public class SpringMvcConfig implements WebMvcConfigurer{
// 自定义视图解析器、拦截器...
}
不加后果:
@RequestMapping失效@RequestBodyJSON 无法解析- 参数绑定、格式化全部失效整个 MVC 瘫痪。
四、它自动帮你开启的关键能力
- 自动注册 Jackson 转换器 → 支持 JSON @RequestBody
- 自动参数绑定 → 普通 Param 自动封装
- 日期转换器、格式校验自动生效
- 支持 REST 风格路径参数
- 开启拦截器、视图解析扩展能力
五、最最重要:SpringBoot 里千万不要乱加!!!
1. SpringBoot 默认自带 MVC 自动配置
WebMvcAutoConfiguration已经帮你自动开启了所有 MVC 功能 ,不用手写 @EnableWebMvc
2. 一旦你在 SpringBoot 加了 @EnableWebMvc
后果:
- 覆盖 Boot 自带自动配置
- 丢失默认 Jackson 全局配置
- 丢失日期自动格式化
- 静态资源默认放行失效
- JSON 乱码、时间格式报错、页面 404 全来
接收请求头和Cookie:
两个专属注解,和你之前学的 @RequestParam / @PathVariable 用法高度相似,一套逻辑直接吃透:
- 接收请求头:@RequestHeader
- 接收 Cookie:@CookieValue
一、通用前置:两个注解共享 3 个属性(一模一样)
@RequestHeader(
value = "头名称", // 指定要获取的Header键名
required = true, // 是否必传,默认true(不传400报错)
defaultValue = "默认值"// 无数据时兜底默认值
)
@CookieValue(属性完全同上)
和@RequestParam语法完全一致,支持默认值 (这点和@PathVariable不一样)。
第一部分:@RequestHeader 接收请求头
1. 核心作用
获取浏览器 / 前端发过来的 HTTP 请求头信息 常见请求头:User-Agent、Host、Referer、token、Content-Type
2. 基础入门示例
① 获取内置标准请求头
@GetMapping("/headerTest")
public String getHeader(
// 获取浏览器设备信息
@RequestHeader("User-Agent") String userAgent,
// 获取访问域名
@RequestHeader("Host") String host
){
System.out.println("浏览器信息:" + userAgent);
System.out.println("访问主机:" + host);
return "success";
}
② 非必传 + 设置默认值
避免请求头不存在直接报错:
@GetMapping("/token")
public String getToken(
@RequestHeader(value = "token", required = false, defaultValue = "guest-token") String token
){
System.out.println("令牌:" + token);
return "success";
}
③ 自定义前端请求头(接口鉴权常用)
前端自定义 Header:Authorization: Bearer 123456abc后端接收:
@PostMapping("/login")
public String auth(@RequestHeader("Authorization") String authHeader){
// 解析token做登录校验
return "校验通过";
}
3. 小细节
请求头名称 不区分大小写 :user-agent 和 User-Agent 等效。
第二部分:@CookieValue 接收 Cookie
1. 核心作用
读取浏览器自动携带到后台的 Cookie 数据 最常用:JSESSIONID 会话 Cookie、自定义业务 Cookie
2. 基础示例 1:获取系统默认 JSESSIONID
java
运行
@GetMapping("/cookie")
public String getSessionCookie(
@CookieValue("JSESSIONID") String sessionId
){
System.out.println("会话Cookie:" + sessionId);
return "success";
}
3. 基础示例 2:自定义 Cookie + 默认值
前端种下 Cookie:username=lisi后端容错接收:
@GetMapping("/userCookie")
public String getUserCookie(
@CookieValue(value = "username", required = false, defaultValue = "匿名用户") String username
){
System.out.println("Cookie用户名:" + username);
return "success";
}
4. 常见坑:Cookie 拿不到原因
- Cookie 的 Path / 域名 和后台接口不匹配;
- 跨域场景没配置允许携带 Cookie;
- Cookie 设置了
HttpOnly:Java 后端依然能读到,前端 JS 读不到。
第三部分:混合实战
5种传参全部混用:
@RestController
@RequestMapping("/api/user")
public class AllParamController {
@PostMapping("/{id}/update")
public Object allParam(
// 1.路径参数
@PathVariable Integer id,
// 2.普通Param参数
@RequestParam String phone,
// 3.JSON请求体
@RequestBody User user,
// 4.请求头Token
@RequestHeader(value = "token",defaultValue = "default123") String token,
// 5.Cookie会话
@CookieValue(value = "JSESSIONID",required = false) String jsessionId
){
System.out.println("路径ID:"+id);
System.out.println("手机号Param:"+phone);
System.out.println("JSON用户:"+user);
System.out.println("请求头Token:"+token);
System.out.println("Cookie会话:"+jsessionId);
return "全部接收成功";
}
}
原生对象获取:
SpringMVC 完全兼容 Servlet 原生对象,直接把 HttpServletRequest/HttpServletResponse/HttpSession****写进 Controller 方法形参,框架自动注入,不用自己 new!
一、支持的所有原生对象(直接注入即可)
在 Controller 方法括号里直接写下面任意对象,Spring 自动帮你绑定当前请求:
HttpServletRequest请求对象(最常用)HttpServletResponse响应对象HttpSession会话对象ServletContext全局上下文对象Cookie[]Cookie 数组
二、1. HttpServletRequest 请求对象 最全用法
基础注入
@GetMapping("/req")
public void getRequest(HttpServletRequest request){
// 所有原生Servlet写法完全通用
}
① 手动获取普通 Param 参数(替代 @RequestParam)
// 等价:@RequestParam String username
String username = request.getParameter("username");
String ageStr = request.getParameter("age");
特点:只能拿key=value 表单 / URL 参数,拿不到 JSON。
② 获取请求头
// 获取单个请求头
String userAgent = request.getHeader("User-Agent");
// 获取所有头名称枚举
Enumeration<String> headerNames = request.getHeaderNames();
③ 原生获取 Cookie
注解只能拿单个 Cookie,原生可以遍历全部:
Cookie[] cookies = request.getCookies();
if(cookies != null){
for (Cookie cookie : cookies) {
if("JSESSIONID".equals(cookie.getName())){
System.out.println("sessionId:" + cookie.getValue());
}
}
}
④ 获取请求路径 / URI
String uri = request.getRequestURI();
String url = request.getRequestURL().toString();
String method = request.getMethod(); // GET/POST
⑤ 请求域存取值
// 存数据到request域
request.setAttribute("msg","原生存入数据");
// 取数据
String msg = (String) request.getAttribute("msg");
⑥ 设置编码(解决 POST 乱码)
request.setCharacterEncoding("UTF-8");
三、2. HttpServletResponse 响应对象
基础注入
@GetMapping("/resp")
public void responseDemo(HttpServletResponse response) throws IOException{
① 手动输出文本 / 页面内容(不走视图解析)
// 设置编码
response.setContentType("text/html;charset=UTF-8");
// 获取输出流
PrintWriter writer = response.getWriter();
writer.write("<h1>原生响应内容</h1>");
writer.flush();
writer.close();
② 原生重定向
// 重定向 两次请求
response.sendRedirect("/index.jsp");
③ 添加 Cookie 到浏览器
Cookie cookie = new Cookie("username","zhangsan");
cookie.setMaxAge(60*60); // 有效期1小时
response.addCookie(cookie);
四、3. HttpSession 会话对象
两种获取方式:
方式 1:直接形参注入(最简单推荐)
@GetMapping("/session")
public void sessionDemo(HttpSession session){
// 存值
session.setAttribute("loginUser","admin");
// 取值
String user = (String) session.getAttribute("loginUser");
// 销毁session
session.invalidate();
}
方式 2:从 request 获取
HttpSession session = request.getSession();
五、4. ServletContext 全局对象(整个项目共享)
@GetMapping("/context")
public void contextDemo(ServletContext context){
// 全局存取值
context.setAttribute("appName","测试项目");
// 获取项目根路径
String path = context.getRealPath("/");
}
也可以从 request 获取:
ServletContext context = request.getServletContext();
六、关键对比:注解接收 VS 原生 API 接收
|---------|----------------|--------------------------|
| 场景 | 注解方式(推荐) | 原生 Servlet API |
| 普通参数 | @RequestParam | request.getParameter() |
| 路径参数 | @PathVariable | 无法原生获取 |
| JSON 参数 | @RequestBody | 原生无法解析 JSON |
| 请求头 | @RequestHeader | request.getHeader() |
| Cookie | @CookieValue | request.getCookies () 遍历 |
| 域对象 | Model | request/session 域 |
共享域对象操作:
SpringMVC 只用 3 个核心域:
- Request 域(一次请求最小范围,最常用)
- Session 域(浏览器会话范围)
- Application 域 (ServletContext)(整个项目全局最大范围)
- Page 域:仅当前页面,SpringMVC 完全不用,直接忽略
一、域通用操作语法(所有域统一格式)
所有域存 / 取 / 删 方法一模一样:
// 存数据
setAttribute("key", 数据);
// 取数据
getAttribute("key");
// 删除数据
removeAttribute("key");
第一部分:Request 域(90% 开发首选)
1. 作用范围
一次请求内有效
- 转发:有效共享
- 重定向:失效
- 请求结束自动销毁
2. 两种操作方式
方式 1:原生 Servlet API 操作
@GetMapping("/reqScope")
public String reqScope(HttpServletRequest request){
// 存
request.setAttribute("msg","Request域数据");
// 取
String msg = (String) request.getAttribute("msg");
// 删
request.removeAttribute("msg");
return "success";
}
方式 2:SpringMVC 封装工具(推荐!更简洁)
Spring 封装了 3 个对象,本质都是操作 Request 域,完全等价 :Model / ModelMap / ModelAndView
① Model(最常用,极简推荐)
@GetMapping("/model")
public String testModel(Model model){
// 存入Request域
model.addAttribute("username","张三");
model.addAttribute("age",20);
return "success"; // 视图可以直接取域中数据
}
② ModelMap
用法和 Model 完全一致:
public String testMap(ModelMap map){
map.addAttribute("info","测试数据");
return "success";
}
③ ModelAndView(数据 + 视图绑定一体)
@RequestMapping("/mav")
public ModelAndView testMav(){
// 1. 创建对象 + 设置视图名
ModelAndView mav = new ModelAndView("success");
// 2. 存数据到Request域
mav.addObject("name","李四");
return mav;
}
✅ 总结:
Model / ModelMap / ModelAndView / request.setAttribute全部都是往 Request 域 放数据,底层完全一样
第二部分:Session 域(会话级别共享)
1. 作用范围
整个浏览器会话有效
- 打开浏览器 → 访问多个接口 → 关闭浏览器前一直存在
- 转发、重定向 都有效
- 不同用户会话隔离
2. 两种操作方式
方式 1:原生 HttpSession 操作
@GetMapping("/session")
public String sessionScope(HttpSession session){
// 存
session.setAttribute("loginUser","admin");
// 取
String user = (String) session.getAttribute("loginUser");
// 删除单个
session.removeAttribute("loginUser");
// 销毁整个会话(退出登录)
session.invalidate();
return "success";
}
方式 2:Spring 专属注解 @SessionAttributes
作用:把 Model 中的数据 自动同步到 Session 域
使用步骤
-
类上加注解
@Controller
@SessionAttributes({"username","age"}) // 指定key存入session
public class ScopeController {@GetMapping("/setSession") public String setData(Model model){ // 先存入Model(Request域) model.addAttribute("username","王五"); // 自动同步到Session域 return "success"; }}
⚠️ 致命坑点(必记)
-
只能同步 Model 里已有的 key,不能直接存 session
-
清除不能 remove,必须用
SessionStatus@GetMapping("/clear")
public String clear(SessionStatus status){
status.setComplete(); // 清除@SessionAttributes存的所有数据
return "redirect:/";
}
第三部分:Application 全局域(ServletContext)
1. 作用范围
整个项目全局共享,服务器启动存在,服务器关闭销毁
- 所有用户、所有会话共享同一份数据
- 慎用:并发多易线程安全问题
2. 原生操作
@GetMapping("/context")
public String appScope(ServletContext context){
// 全局存
context.setAttribute("projectName","SpringMVC学习");
// 全局取
String name = (String) context.getAttribute("projectName");
return "success";
}
也可以从 request 获取:
ServletContext context = request.getServletContext();
第四部分:转发 & 重定向 域数据存活规则
1. 转发 forward
1 次请求:✅ Request 域:保留共享✅ Session 域:保留✅ Application 域:保留
2. 重定向 redirect
2 次新请求:❌ Request 域:全部丢失✅ Session 域:保留✅ Application 域:保留
第五部分:三大域完整对比表
|---------------|-----------|--------------|--------|---------|-------------|
| 域对象 | 作用范围 | 存活时长 | 转发 | 重定向 | 使用场景 |
| Request 域 | 单次请求 | 请求结束销毁 | ✅有效 | ❌失效 | 页面临时传值、视图渲染 |
| Session 域 | 单个用户浏览器会话 | 浏览器关闭 / 过期销毁 | ✅有效 | ✅有效 | 登录用户信息、购物车 |
| Application 域 | 整个项目所有用户 | 服务器启停 | ✅有效 | ✅有效 | 全局配置、项目常量 |
Spring响应式数据:
快速返回逻辑视图:
步骤:
1.先导依赖:
<!-- jsp需要依赖! jstl-->
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>3.0.0</version>
</dependency>
jsp页面创造:
建议创建在WEB-INF下,避免外部直接访问
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${data}</h1>
</body>
</html>
${}这个里面写的就是在controller层里写的要加载的名字
配置jsp视图解析器:
@Configuration
@ComponentScan("com.atguigu.jsp")
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
private static final Log log = LogFactory.getLog(MvcConfig.class);
//视图解析器,指定前后缀
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
//registry可以快速添加前后缀
registry.jsp("/WEB-INF/views/", ".jsp");//这里面可以快速指定前后缀,只要前后缀等于目标视图的完整路径就可以
}
}
@Configuration
@ComponentScan("com.atguigu.jsp")
@EnableWebMvc
写视图解析器的这个类要实现WebMvcConfigurer这个接口,
放到ioc容器里去,并设置拦截的请求:
public class SpringMVCInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MvcConfig.class};//把配置文件加载到在ioc容器中
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};//设置成拦截所有请求
}
}
返回视图:
@Controller
@RequestMapping("index")
public class JspController {
@GetMapping("jsp")
public String index(HttpServletRequest request) {
request.setAttribute("data","hello jsp!!!");
System.out.println("JspController.index");
return "index";
}
}
最后那个return要写的是jsp那个文件的名字:

转发和重定向:
在 Spring MVC 中,Handler 方法返回值来实现快速转发,可以使用 redirect 或者 forward 关键字来实现重定向。
@RequestMapping("/redirect-demo")
public String redirectDemo() {
// 重定向到 /demo 路径
return "redirect:/demo";
}
@RequestMapping("/forward-demo")
public String forwardDemo() {
// 转发到 /demo 路径
return "forward:/demo";
}
//注意: 转发和重定向到项目下资源路径都是相同,都不需要添加项目根路径!填写项目下路径即可!
总结:
- 将方法的返回值,设置String类型
- 转发使用forward关键字,重定向使用redirect关键字
- 关键字: /路径
- 注意:如果是项目下的资源,转发和重定向都一样都是项目下路径!都不需要添加项目根路径!
|-----------------------------|---------|-----|------|
| 写法 | 含义 | 地址栏 | 数据共享 |
| return "forward:/success" | 服务器内部转发 | 不变 | 共享 |
| return "redirect:/login" | 客户端重定向 | 改变 | 不共享 |
返回json数据:
前置准备:
导入依赖和添加json数据转换器:
导入jackson依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>
添加json数据转化器
@EnableWebMvc
//TODO: SpringMVC对应组件的配置类 [声明SpringMVC需要的组件信息]
//TODO: 导入handlerMapping和handlerAdapter的三种方式
//1.自动导入handlerMapping和handlerAdapter [推荐]
//2.可以不添加,springmvc会检查是否配置handlerMapping和handlerAdapter,没有配置默认加载
//3.使用@Bean方式配置handlerMapper和handlerAdapter
@EnableWebMvc //json数据处理,必须使用此注解,因为他会加入json处理器
@Configuration
@ComponentScan(basePackages = "com.atguigu.controller") //TODO: 进行controller扫描
//WebMvcConfigurer springMvc进行组件配置的规范,配置组件,提供各种方法! 前期可以实现
public class SpringMvcConfig implements WebMvcConfigurer {
}
然后在类上加注解:
|-------------------------------------|---------|------------------------------------------------------|
| 注解 | 位置 | 作用 |
| @ResponseBody @Controller(这两个一起用) | 方法 / 类上 | 单独使用:标记方法返回值直接写入响应体,自动转为 JSON |
| @RestController | 类上 | 组合注解 = @Controller + @ResponseBody ,开发首选 |
@ResponseBody
@Controller
//或者直接加@RestController
@RequestMapping("json")
public class JsonController {
@GetMapping("data")
public User data(){
User user = new User();
user.setAge(18);
user.setName("妄竹");
return user;//直接返回user,然后加一个@ResponseBody,这样就会直接返回一个json串
}
@GetMapping("data2")
public List<User> data1(){
User user = new User();
user.setAge(18);
user.setName("妄竹安");
List<User> list = new ArrayList<>();
list.add(user);
return list;
}
}
静态资源处理:
很简单就是开启寻找静态资源的开关:
在MvcConfig文件中:
//开启静态资源查找
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
这个方法里面就是写了一个转发
RESTFul风格接口设置:

ESTful 的优势
万物皆资源 (用户、订单、商品都是资源),用名词 定义资源,用HTTP 方法表示操作:
查询用户:GET /users/1
新增用户:POST /users
修改用户:PUT /users/1
删除用户:DELETE /users/1
优点:见 URL 知含义、统一标准、前后端协作更高效。
核心三要素
- Resource(资源) :所有数据都是资源(用户、订单、图片),用名词复数 表示(
/users、/orders); - URL(资源地址) :定位资源的唯一标识,无动词、无后缀;
- HTTP Method(操作方式):用固定方法表示增删改查,不自定义操作名。
举例:
@RestController
@RequestMapping("user")
public class UserController {
@GetMapping
public List<User> page(@RequestParam(required = false,defaultValue = "1") int page,@RequestParam(required = false,defaultValue = "10") int size){
System.out.println("page:"+page + "size:"+size);
return null;
}
@PostMapping
public User save(@RequestBody User user){
return user;
}
@GetMapping("{id}")
public User detail(@PathVariable Integer id){
return null;
}
@PutMapping
public User updata(@RequestBody User user){
return user;
}
@DeleteMapping("{id}")
public User delete(@PathVariable Integer id){
return null;
}
@GetMapping("seach")
public List<User> seach(String keywork,
@RequestParam(required = false,defaultValue = "1") int page,
@RequestParam(required = false,defaultValue = "10") int size){
return null;
}
}
全局异常处理:
第一步:先定义一个全局异常处理类
第二步:在这个全局异常处理类中声明处理方法
@ControllerAdvice//该注解的意思是:全局异常发生了,就会走此类的handler
@RestControllerAdvice //这个注解的意思就是加了:@ControllerAdvice和@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(ArithmeticException.class)//这个异常就是标识专门处理哪种异常,括号里写的就是要处理的异常
public Object ArithmeticExceptionHandler(ArithmeticException e) {
//自定义处理异常即可
return null;
}
@ExceptionHandler(Exception.class)
public Object ExceptionHandler(Exception e){
String msg= e.getMessage();
System.out.println(msg);
return msg;
}
}
拦截器的概念和基本使用:
拦截器的作用和javaweb的filter一模一样,只是它实在springmvc环境下使用的
创建一个拦截器:
public class Myinterceptor implements HandlerInterceptor {
//在执行handler前,调用的拦截器方法
//编码格式处理,登录保护,权限处理
/**
* @param request 请求对象
* @param response 响应对象
* @param handler handler就是我们要调用的方法对象
* @return true 放行 false 拦截
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
/** 当handler执行完毕后,就会触发的方法,此时已经没有拦截的机制了,而且此方法只有preHandler返回值是true时,才会触发
* 对结果进行处理,敏感词汇检查
* @param request 请求
* @param response 响应
* @param handler handler方法
* @param modelAndView 返回的视图和共享数据的对象
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Myinterceptor postHandle");
}
/** 整体处理完毕之后,才会触发的方法
*
* @param request
* @param response
* @param handler
* @param ex handler报错之后的异常对象
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Myinterceptor afterCompletion");
}
}
把它注入到SpringMvc框架中去拦截请求
@Configuration
@ComponentScan(basePackages = {"com.atguigu.controller","com.atguigu.error"})
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
private static final Log log = LogFactory.getLog(MvcConfig.class);
// 视图解析器,指定前后缀
// @Override
// public void configureViewResolvers(ViewResolverRegistry registry) {
// //registry可以快速添加前后缀
// registry.jsp("/WEB-INF/views/", ".jsp");//这里面可以快速指定前后缀,只要前后缀等于目标视图的完整路径就可以
//
//
// }
//开启静态资源查找
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置方案1:把这个拦截器注册到SpringMvc框架中去拦截请求,拦截全部请求
registry.addInterceptor(new Myinterceptor());
}
}
另外两种配置方案:
//配置方案2:指定地址拦截 .addPathPatterns("/*/**")
// * 任意一层字符串 ** 任意多层字符串
registry.addInterceptor(new Myinterceptor()).addPathPatterns("/user/**");
//配置方案3:排除拦截 排除的地址应该在拦截地址内部
registry.addInterceptor(new Myinterceptor()).addPathPatterns("/user/**").excludePathPatterns("/user/data");
多个拦截器
1.拦截器的执行顺序由注册顺序决定:
preHandle:正序执行(先注册先执行)postHandle:逆序执行(后注册先执行)afterCompletion:逆序执行(后注册先执行)
2. 正常流程(所有 preHandle 返回 true)
假设有 3 个拦截器:Interceptor1 → Interceptor2 → Interceptor3(注册顺序)
plaintext
1→preHandle → 2→preHandle → 3→preHandle
→ Controller执行
→ 3→postHandle → 2→postHandle → 1→postHandle
→ 视图渲染
→ 3→afterCompletion → 2→afterCompletion → 1→afterCompletion
3. 中断流程(某个 preHandle 返回 false)
如果 Interceptor2 的 preHandle 返回 false:
plaintext
1→preHandle(放行)→ 2→preHandle(拦截)
→ 直接执行 1→afterCompletion
→ 请求结束
✅ 结论:只有返回 true 的拦截器,才会执行 afterCompletion。
参数校验注解:



举例:
实体类:
public class User {
//@Notnull 表示包装类不为空
//@NotBlank 表示集合不为空
@NotNull//这个注解表示,字符串不为空
private String name;
@Length(min = 6)
private String password;
@Min(1)
private int age;
@Email
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
/** 步骤一:实体类属性添加校验注解
* 步骤二:handler方法里 要再加一个@Validated注解
* 步骤 : param | json 校验注解都有效果
* 如果是json参数,还需要再加一个@RequestBody
*
* 捕捉错误绑定错误信息:
* 1.handler(校验对象,BindingResult result) 要求:BindingResult 必须紧挨着校验对象
*
*/
//接收用户数据,用户有校验注解
@PostMapping("register")
public Object register(@Validated User user, BindingResult result){
if(result.hasErrors()){
//如果有绑定错误,就不直接返回,自定义要返回的内容
Map data = new HashMap();
data.put("code",400);
data.put("msg","校验参数异常");
return data;
}
System.out.println(user);
return user;
}