【JavaEE07-后端部分】SpringMVC02-SpringMVC第二大核心处理请求

上一期呢我们说到SpringMVC的第一大核心知识:建立连接,上一期我们说学习Spring MVC主要是学习三个东西:建立连接、请求和响应。那么建立连接,我们在上一期介绍完后,本期我们来介绍请求


请求处理:参数传递与接收

我们使用Postman发送请求和使用客户端发送请求对于后端而言是一样的,

就像外卖和堂食对于我们厨师来说是一样的。

请求处理的核心是"前端传递参数,后端接收参数",Spring MVC支持多种参数传递方式,后端可灵活接收。

1 传递单个参数

后端直接在方法中定义与前端参数名一致的形参,即可接收参数:

那么我先看前端的请求:

我们看后端的代码:

java 复制代码
package com.zhongge.demo;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName RequestController
 * @Description TODO 请求控制器
 * @Author 笨忠
 * @Date 2026-02-06 15:26
 * @Version 1.0
 */
@RestController//加上这个注解代表将我们的对象交给Spring管理 带有Rest代表的是所有的方法返回的是JSON格式
@RequestMapping("/request")//类路径
public class RequestController {

    //由于我们还没有说响应所以我们的方法都先返回字符串
    @RequestMapping("/r1")//方法路径
    public String r1(String name){
        //注意的是我们的name参数一定是从我们的方法形参列表中获取的
        return "一个参数:" + name;
        //后续我们还可以将name存入到我们的数据库中去进行一个对比
    }

}

请求的结果:

注意事项:

1.参数名必须与前端一致,否则无法获取参数。【这个在后面解释】


2.基本类型(除boolean外)接收参数时,参数必须传递,否则报500错误;类型不匹配时,报400错误。

比如现在我使用的是基本类型

java 复制代码
   @RequestMapping("/r2")
    public String r2(int num){
        return "一个参数得到的是值:" + num;
    }

1)如果正确传递

2)如果不传递,后台报错

3)传递类型不匹配的话,你们就是前端报错


3,经过上述因此我们建议使用包装类型(如Integer、String)接收参数,参数未传递时会赋值为null,避免报错。原因就是我们SpringMVC接受到的参数都是String类型,他回去给你去转,所以对于包装类型,如果转不了的话,那么就是默认为null

java 复制代码

此时就算你的参数没有给,那么都不会报错

2 传递多个参数

与单个参数接收方式一致,定义多个形参,前端按参数名传递即可,参数顺序不影响接收结果:

后端代码:

java 复制代码
     //传递多个参数,用户名和密码
    @RequestMapping("/r4")
    public String r4(String username, String password){
        return "用户名是:" + username + "  密码是:" + password;
    }

使用GET方式请求:


使用表单形式+POST方式请求


3 传递对象

当参数较多时,可将参数封装为Java对象,Spring MVC会根据参数名自动绑定到对象的属性上:

  1. 定义实体类(如User),包含对应属性、getter/setter方法和toString方法。
  2. 后端方法直接接收该对象作为参数:
    实体类
java 复制代码
package com.zhongge.demo;

/**
 * @ClassName User
 * @Description TODO 实体类 User
 * @Author 笨忠
 * @Date 2026-02-08 16:14
 * @Version 1.0
 */
public class User {
    //用户名
    private String username;
    //年龄
    private Integer age;//注意 我们建议使用包装类

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    //重写toString方法

    @Override
    public String toString() {
        return "User{" +
                "用户名='" + username + '\'' +
                ", 年龄=" + age +
                '}';
    }
}

处理器方法

java 复制代码
// URL:http://127.0.0.1:8080/param/m3?id=5&name=zhangsan&password=123456
@RequestMapping("/m3")
public Object method3(Userp){
    return p.toString();
}

说明:未传递的属性,引用类型赋值为null,基本类型赋值为默认值(如int为0)。


注意事项:

实体类要有get和set方法,否则将就会读取不到数据导致数据为null,因为底层是反射机制。

java 复制代码
package com.zhongge.demo;

/**
 * @ClassName User
 * @Description TODO 实体类 User
 * @Author 笨忠
 * @Date 2026-02-08 16:14
 * @Version 1.0
 */
public class User {
    //用户名
    private String username;
    //年龄
    private Integer age;//注意 我们建议使用包装类

   /* public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
*/
    //重写toString方法

    @Override
    public String toString() {
        return "User{" +
                "用户名='" + username + '\'' +
                ", 年龄=" + age +
                '}';
    }
}

如果没有实体类,那么此时就是


4 后端参数重命名(@RequestParam)

解释:为什么前端的参数和后端形参需要一致?

如图所示:我们前端传递的参数是username,password。

那么此时我后端的处理器方法代码如下:

java 复制代码
	@RequestMapping("/r4")
    public String r4(String username, String password){
        //接受的参数是和前端的一致的username和password
        return "用户名是:" + username + "  密码是:" + password;
    }

那么他具体的处理逻辑是什么呢?我们看下图:

首先我的前端传递的是username和password,那么此时我们就会使用一个map集合将他存起来,然后的话,我们的后端就根据形参列表中参数名字去map集合中进行比对,如果找到了,那么就取到对应的值了。


如果前端的参数和我们后端的形参名字不同的话,那么此时我们就无法取到值,于是我们username和password的值都是空的null,首先看现象

java 复制代码
  //参数不一致的情况
    @RequestMapping("/r6")
    public String r6(String name, String psd){
        return "用户名是:" + name + "  密码是:" + psd;
    }

现象

原理如图所示:

由于我后端使用的是name和psd去map集合里面去寻找,所以我们最终没有找到对应的值,所以才会读到null。


那么我们怎么解决这样的问题呢?

我们使用@RequestParam【请求参数】

当前端传递的参数名与后端接收的参数名不一致时,使用@RequestParam注解重命名:

java 复制代码
	//使用RequestParam注解
 	@RequestMapping("/r6")
    public String r6(@RequestParam("username") String name,@RequestParam("password") String psd){
        return "用户名是:" + name + "  密码是:" + psd;
    }

现象:此时就可以正确的取到数据了

原理如图所示:

就是说使用@RequestParam("username") String name代表的是将username拿去集合中找到key为username的值,如果找到了就将这个值赋值给name变量。


@RequestParam注解的详解

java 复制代码
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean required() default true;

    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

1.第一个属性value和name

  • value 和 name 是别名关系,功能完全相同

  • 如果不指定,默认使用方法参数名作为请求参数名

2.required 属性 - 参数必要性控制

  • 默认值为 true,意味着默认所有参数都是必需的

  • 当 required=true 且参数未传递时,Spring会抛出异常

3.defaultValue 属性 - 默认值设置

  • defaultValue 的值永远是字符串
  • Spring会自动进行类型转换(String → 目标类型)
  • 设置了 defaultValue 时,required 属性自动视为 false
  • 默认值对空字符串也生效

关键说明:

  • @RequestParam修饰的参数默认是必传的,未传递会报400错误。
  • 非必传参数设置:添加required=false(如@RequestParam(value = "time", required = false))。
    也就是说你使用这个注解,那么默认这个参数你就必须传递
    如果不传递就会报错
    此时如果你将required属性设置为false,那么就代表这个参数不是必须传递的,此时就不会报错了

    现象:
    那么注意的是:比如我们搞一个登录功能,那么用户名和密码是必须传递的,那么此时就可以使用@RequestParam来限制你这个参数必须传递。

5 传递数组

后端定义数组类型形参,前端传递多个同名参数即可,Spring MVC会自动封装为数组:

后端代码:

java 复制代码
 	//传递数组
    @RequestMapping("/r7")
    public String r7(String[] arr) {
        return "数组中的值是:" + Arrays.toString(arr);
    }

1)第一种请求方式:单独的输入多个arr的值

2)只输入一个arr,然后使用逗号隔开

补充:前端也可通过"arrayParam=zhangsan,lisi,wangwu"的方式传递,后端同样能接收。

6 传递集合

集合参数需配合@RequestParam注解使用,Spring MVC会将多个同名参数封装为集合:

java 复制代码
  	@RequestMapping("/r8")
    public String r8( List<String> list) {
        return "集合中的值是:" + list;
    }

那么此时我们如果想使用数组一样的方式处理的话,那么后端就会报错

后端报错信息:


那么原因是什么?
上述的参数传递方式会默认当做数组来处理,所以如果你需要将他变为集合的话,那么此时得注意加上一个参数绑定注解@RequestPrame

java 复制代码
    @RequestMapping("/r8")
    public String r8(@RequestParam List<String> list) {
        return "集合中的值是:" + list;
    }

结果:此时就不会报错了

注意:@RequestParam List<String> list @RequestParam 不写属性value,那么value的值默认就是参数名字list


7 传递JSON数据(@RequestBody)

JSON就是JavaScript的对象表示法。

JSON是前后端数据交互的常用格式,本质是字符串,支持对象、数组等复杂结构,Spring MVC通过@RequestBody注解接收JSON对象

JSON核心基础

  • 语法:数据以键值对形式存在,用{}表示对象,[]表示数组,键值(key/value)对用逗号分隔,属性和值之间使用冒号分开,键key使用引号,值value中如果是字符串就是用引号。
  • 优点:简单易用、跨平台、轻量级、易于扩展、安全性高。
  • JSON与Java对象互转:Spring MVC集成jackson-databind工具,所以说我们如果写的是Spring项目的话, 那么你就不用手动去将Java和JSON进行转换了,因为SpringMVC已经为我们做好了,通过ObjectMapper的writeValueAsString(对象转JSON)和readValue(JSON转对象)方法实现。

jackson的使用:

对象变为JSON
对象

java 复制代码
package com.zhongge.demo;

/**
 * @ClassName User
 * @Description TODO 实体类 User
 * @Author 笨忠
 * @Date 2026-02-08 16:14
 * @Version 1.0
 */
public class User {
    //用户名
    private String username;
    //年龄
    private Integer age;//注意 我们建议使用包装类

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    //重写toString方法

    @Override
    public String toString() {
        return "User{" +
                "用户名='" + username + '\'' +
                ", 年龄=" + age +
                '}';
    }
}

补充单元测试

对应的代码

java 复制代码
   /**
     * 对象转为JSON
     */
    @Test//加这个注释代表的是单元测试
    void TestObjectToJson() throws JsonProcessingException {
        //首先做对象和JSON之间转换的人是:ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();

        //然后你是需要Java对象转为JSON的
        //所以现在就需要先创建一个Java对象
        User user = new User();
        user.setUsername("李四");
        user.setAge(10);

        //那么现在就开始将Java对象转为JSON
        //使用ObjectMapper中的writeValueAsString
        //writeValueAsString从后向前翻译就是将值写为字符串,而我们的Java变为JSON就是将值作为字符串
        String json = objectMapper.writeValueAsString(user);

        //最后将JSON对象打印出
        System.out.println(json);
    }

对应的结果就是如下:


JSON变对象

对应的代码:

java 复制代码
/**
     * JSON变为Java对象
     */
    @Test
    void testJsonToObject() throws JsonProcessingException {
        //首先做对象和JSON之间转换的人是:ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();

        //然后你得有一个JSON字符串
        String json = "{\"username\":\"李四\",\"age\":10}";

        //那么此时就调用我们的ObjectMapper中的readValue方法
        //readValue代表的是将 某某JSON字符串变为 某某对象[使用的是类对象]
        User user = objectMapper.readValue(json, User.class);

        //将对象输出
        System.out.println(user);
    } 

对应的结果:


JSON表示一个对象
json 复制代码
{
  "id": 1001,
  "username": "张三",
  "age": 25,
  "isVip": true,
  "email": null,
  "address": {
    "city": "北京",
    "street": "海淀区中关村"
  }
}

对应的Java对象

java 复制代码
public class User {
    private Integer id;
    private String username;
    private Integer age;
    private Boolean isVip;
    private String email; // 为 null
    private Address address; // 嵌套对象

    // getters and setters...
}

public class Address {
    private String city;
    private String street;
    // getters and setters...
}

表示一个数组
json 复制代码
["苹果", "香蕉", "橙子", "葡萄"]

对应Java中

java 复制代码
// 可能是 List<String>
List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子", "葡萄");

数组里包含多个对象
json 复制代码
[
  {
    "id": 1001,
    "username": "张三",
    "age": 25
  },
  {
    "id": 1002,
    "username": "李四",
    "age": 30
  },
  {
    "id": 1003,
    "username": "王五",
    "age": 28
  }
]

对应的Java中

java 复制代码
// 通常是 List<User>
List<User> userList = new ArrayList<>();
// ... 向列表中添加 User 对象

对象里包含数组
json 复制代码
{
  "departmentName": "技术部",
  "employeeCount": 3,
  "employees": [
    {
      "name": "程序员A",
      "title": "工程师"
    },
    {
      "name": "程序员B",
      "title": "高级工程师"
    },
    {
      "name": "程序员C",
      "title": "架构师"
    }
  ]
}

对应的Java中

java 复制代码
public class Department {
    private String departmentName;
    private Integer employeeCount;
    private List<Employee> employees; // 对象内包含集合
    // getters and setters...
}

public class Employee {
    private String name;
    private String title;
    // getters and setters...
}

更复杂的嵌套(对象套数组,数组里对象再套数组...)
json 复制代码
{
  "articleId": 1,
  "title": "JSON 入门指南",
  "comments": [
    {
      "userId": "user123",
      "content": "好文章!",
      "replies": [ // 评论下还有回复(数组)
        {
          "userId": "user456",
          "content": "同意!"
        }
      ]
    },
    {
      "userId": "user789",
      "content": "例子很详细。",
      "replies": [] // 该评论暂无回复,空数组
    }
  ]
}

后端接收JSON代码示例

后端实体类

java 复制代码
package com.zhongge.demo;

/**
 * @ClassName User
 * @Description TODO 实体类 User
 * @Author 笨忠
 * @Date 2026-02-08 16:14
 * @Version 1.0
 */
public class User {
    //用户名
    private String username;
    //年龄
    private Integer age;//注意 我们建议使用包装类

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
    //重写toString方法

    @Override
    public String toString() {
        return "User{" +
                "用户名='" + username + '\'' +
                ", 年龄=" + age +
                '}';
    }
}

前端使用Postman来传递JSON格式的对象

点击body->raw->选择json

然后输入json对象


后端的处理:后端的控制器方法如果需要商品SpringMVC为我们接收json对象并且处理他,那么此时就使用@RequestBody注解来标记,代表我们需要SpringMVC为我们做这件事情

java 复制代码
	//接受json对象参数
    @RequestMapping("/r9")
    public String r9(@RequestBody User user) {
        return "由SpringMVC将json变为对象的是:" + user.toString();
    }

处理的结果:

注意:@RequestBody作用于请求正文【就是请求体body】,参数必须写在请求正文中,不能通过URL拼接;去掉@RequestBody则无法接收JSON参数。


注意事项:SpringMVC将json转对象使用的优先级情况如下

java 复制代码
// 优先级排序(从高到低):
1. @JsonCreator 注解的构造器/工厂方法
2. 公有全参构造器(Jackson 2.7+ 可自动检测)
3. 无参构造器 + setter 方法 ← 最常见的默认方式
4. 无参构造器 + 字段直接赋值(需配置)

8 获取URL路径参数(@PathVariable)

PathVariable翻译为路径参数/路径变量,代表的是从URL路径中获取参数,也就是我SpringMVC从URL路径中将参数给拿到。这种参数称为路径参数,他通常用于唯一的ID。

那么主要的步骤就两个:
1.自定义URL

java 复制代码
@RequestMapping("/r10/{userId}/{userName}")//定义URL:定义出两个参数

2.@PathVariable使用注解,从你定义的URL中获取参数的值

java 复制代码
  //注意的是:@PathVariable是参数注解有几个参数就得有几个注解
  public String r10(@PathVariable Integer userId,//使用@PathVariable注解接收自定义url中的参数
                      @PathVariable String userName) {
        return "";
    }

完整的代码:

java 复制代码
  //使用@PathVariable注解从自定义的URL中获取参数
    @RequestMapping("/r10/{userId}/{userName}")//定义URL:定义出两个参数
    public String r10(@PathVariable String userName,//使用@PathVariable注解接收自定义url中的参数
                      @PathVariable Integer userId) {
        return "从自定义URL中获取的参数是:" + "用户ID:" + userId + " 用户名:" + userName;
    }

结果:


注意事项:
前端传递的参数和后端自定义路径中的参数必须一一对应
后端自定义参数和方法形参中的顺序可以不用对应

比如:你的前端参数和后端的URL定义不匹配就会报400


9 上传文件(@RequestPart和MultipartFile的使用)

那么我们在开发中上传文件也是一个很关键的事情,在我们的SpringMVC中上传文件的操作就相当的简单,因为他已经为我们封装好了,我们只需要去调用就行了,我们使用MultipartFile类操作文件说白了就是使用这个类来接受文件,将这个类放在参数部分,MultipartFile翻译为多部分文件,主要就是用来操作文件的,底层封装了IO操作。

举个例子:

前端使用Postman上传文件,我们使用的是body->form-data

后端使用MultipartFile类来接收文件

java 复制代码
    @RequestMapping("/r11")
    public String r11(MultipartFile file) {
        //输出文件名
        System.out.println(file.getOriginalFilename());
        
        return "后端成功接收到文件:" + file.getOriginalFilename();
    }

结果:

后端控制台结果:


后端使用MultipartFile将文件收到之后,后端如何将文件存在服务器硬盘上呢?
使用的是MultipartFile中的transferTo(文件对象),底层封装了一系列的文件IO读写的相关操作,我们只需要掉用就可以完成文件的保存到服务器

transferTo翻译为移动到

代码:

java 复制代码
   //上传文件
    @RequestMapping("/r11")
    public String r11(MultipartFile file) throws IOException {
        //输出文件名
        System.out.println(file.getOriginalFilename());

        //将文件保存到服务器中
        file.transferTo(new File("D:/code/" + file.getOriginalFilename()));

        return "后端成功接收到文件:" + file.getOriginalFilename();
    }

结果:文件保存成功


注意:可以使用@RequestPart注解来重命名参数

比如我的前端参数不是file而是改为f,那么此时就会报错

此时我就在后端使用该注解将参数命名

java 复制代码
    /**
     * 使用@RequestPart注解将参数重命名
     */
    @RequestMapping("/r11")
    public String r11( @RequestPart("f") MultipartFile file) throws IOException {
        //输出文件名
        System.out.println(file.getOriginalFilename());

        //将文件保存到服务器中
        file.transferTo(new File("D:/code/" + file.getOriginalFilename()));

        return "后端成功接收到文件:" + file.getOriginalFilename();
    }

此时结果就正确了


测试:使用Postman的form-data格式,key设为file(与注解中一致),选择文件上传即可。


总结:

HTTP 请求的参数可以放在什么地方?

  1. URL资源路径【URL上除去查询字符串】
  2. URL查询字符串【问号?后面的,该参数称为请求参数】
  3. header
  4. 请求正文body【该参数称为请求参数】

至此,请求处理的核心知识已基本讲介绍完毕。剩余还有两项关键内容:获取 Cookie 与 Session、获取请求头header,其中 Cookie 和 Session 为重点内容,具体细节将在下期详细介绍。最后欢迎点赞、关注、收藏,感谢各位老铁。

相关推荐
青云计划10 小时前
知光项目知文发布模块
java·后端·spring·mybatis
赶路人儿10 小时前
Jsoniter(java版本)使用介绍
java·开发语言
探路者继续奋斗11 小时前
IDD意图驱动开发之意图规格说明书
java·规格说明书·开发规范·意图驱动开发·idd
消失的旧时光-194311 小时前
第十九课:为什么要引入消息队列?——异步系统设计思想
java·开发语言
yeyeye11111 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
A懿轩A11 小时前
【Java 基础编程】Java 面向对象入门:类与对象、构造器、this 关键字,小白也能写 OOP
java·开发语言
乐观勇敢坚强的老彭12 小时前
c++寒假营day03
java·开发语言·c++
biubiubiu070612 小时前
谷歌浏览器无法访问localhost:8080
java
大黄说说12 小时前
新手选语言不再纠结:Java、Python、Go、JavaScript 四大热门语言全景对比与学习路线建议
java·python·golang
烟沙九洲12 小时前
Java 中的 封装、继承、多态
java