@RequestParam、@RequestBody、@ModelAttribute 注解的区别

Spring 中 @RequestParam、@RequestBody、@ModelAttribute 注解的区别。

引言:在 Spring MVC中, 有时我们会需要通过客户端传入得到相关参数来进行相应的处理。一般的, 对于特定的数据的处理,如书籍按照 ISBN 查找,用户信息按照 ID 来获取… 这些参数仅仅需要相关的主键即可处理,因此只需要添加相关的路径变量作为参数即可(PathVariable)。但是,有时候, 我们需要很多个参数, 此时就需要我们把这些相关的参数封装起来, 作为一个参数传递。这时, 我们便需要用到 @RequestParam@RequestBody@ModelAttribute 注解来处理相关的参数传递。

本文用到的参数传递对象:

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class SearchPage {
    @JsonProperty("startLocation")
    private Integer startLocation;

    @JsonProperty("size")
    private Integer size;

    @JsonProperty("sortCol")
    private String sortCol;
}
  1. 不添加注解的参数
    实际上, 即时不添加相关的注解,依旧可以实现相关参数对象的传递,但是这时的参数传递是基于传入的参数的名称来注入到相关的参数对象中的。如下示例所示:
@PostMapping(path = "/demo_3")
public String demo_3(SearchPage searchPage) {
    log.info("SearchPage: " + searchPage.toString());
    return "Hello World!";
}

这个控制器接受相关的 SearchPage 对象参数信息, 这时控制器将会将得到的请求参数的名称与该对象的属性字段匹配,然后注入字段名称相同的属性的属性值。此时按照一般的传递参数的方式传递相关的属性字段值:

 curl -XPOST "http://127.0.0.1:3030/demo_3?startLocation=0&size=20&sortCol=BookName" -H "Content-Type: application/json"

从控制台上可以看到类似以下输出:
在这里插入图片描述
这说明我们传递的参数已经注入到相关的参数对象中了。
如果我们们修改相关的参数对象的 Setter 方法, 将 SearchPage 类修改为以下类:

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@ToString
public class SearchPage {
    @Getter
    @Setter
    @JsonProperty("startLocation")
    private Integer startLocation;

    @Getter
    @JsonProperty("size")
    private Integer size;

    @Getter
    @Setter
    @JsonProperty("sortCol")
    private String sortCol;
}

在这里我们没有给 size 字段添加对应的 Setter 方法, 再次编译运行这个程序,执行之前的访问命令:

curl -XPOST "http://127.0.0.1:3030/demo_3?startLocation=0&size=20&sortCol=BookName" -H "Content-Type: application/json"

在控制台上可以看到相应类似的输出:
在这里插入图片描述
size 属性并没有设置相关的属性值, 这说明不带注解修饰的参数对象的传递是按照相对应的属性字段名的 Setter方法来注入相关属性值的。(个人推测这可能是通过 Java 的反射机制来实现的)

这种不带注解参数对象的传递是只针对请求时输入的参数键值对来注入的。当发送多个请求参数时,控制器会依次遍历这个控制器方法的所有参数对象,再依次遍历参数对象的属性字段,从相关的请求参数键值对中找到相匹配的键值对,再将对应的键值对的值注入到相对应的参数对象的对应字段属性中。

  1. @ModelAttribute 注解修饰的参数
    官方文档:@ModelAtribute 官方文档
    与不加任何注释的控制器参数修饰类似,@ModelAttribute 注解修饰的参数对象也会从输入的参数的键值对中找到相匹配的参数的键值对,再将相匹配的键值对的值注入到该属性字段中。但是使用 @ModelAttribute注解也有一些独特的地方。首先,它可以通过指定 name 属性来匹配相对应的参数;其次,它可以接受 Json 类型的参数转换为对应的参数对象(不能直接转换)。具体的转换请看下文关于 @RequestParam 注解修饰的参数的说明。

  2. @RequestParam 注解修饰的参数
     使用 @RequestParam 注解修饰时, 可以通过设置对应的 name 属性来获取指定的请求参数。也可以设置 required 属性来设置这个请求参数是否是需要的,由于一般都要求传入相关的参数,因此这个属性值默认是为 true 的。当然, 也可以设置 defaultValue 属性来设置默认值。
     一般情况下, 请求的参数都是会被转化成为 String 类型,因此如果这里的参数是其它类型的话, 那么在传入对应参数时将会抛出一个转换异常。如果想要解决这个问题, 可以考虑创建一个组件,用于将传入的参数字符串对象转变为对应的对象参数。

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

@Component
public class StringToSearchPageConvert implements Converter<String, SearchPage> {
    @Override
    public SearchPage convert(String s) {
        try {
	        // Jackson JSON 转变为对象的 ObjectMapper 对象
            ObjectMapper mapper = new ObjectMapper();
            return mapper.readValue(s, SearchPage.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

创建这个组件后, 对于使用了 @ModelAttribute@RequestParam 注解修饰的 SearchPage 类的参数就可以 “直接” 转变为 SearchPage 类的对象参数了。

不用担心这个组件是如何运行的, 由于Spring 强大的 IOC 容器的存在, 使用 @ModelAttribute@RequestParam 注解修饰的参数对象在获取时将会检查是否有组件实现了相关的对象的转换, 从而使用这个组件进行对象的转换而不是直接转换。

  1. @RequestBody 注解修饰的参数
     这个注解只能用于 POST 请求中, 这是由于只有 POST 请求才能带有 body 的数据体请求(GET请求可以带有 body 数据体, 但是没有实际意义)。这个注解修饰的参数对象将会直接进行请求参数项参数对象的转换。
     需要注意的是, 这个注解转换的是 JSON 类型的请求体,因此请求的数据体应当是 “application/json”类型的。

项目地址:https://github.com/LiuXianghai-coder/SpringStudy/tree/main/demo_1

你可能感兴趣的:(java,spring)