@RequestBody注解的使用及源码解析

@RequestBody注解的使用及源码解析

前言

@RequestBody 注解是我们进行JavaEE开发,最常见的几个注解之一,这篇博文我们以案例和源码相结合,帮助大家更好的了解 @RequestBody 注解

使用案例

1.自定义实体类

@Data

@NoArgsConstructor

@AllArgsConstructor

public class User {

private String username;

private String password;

@Override

public String toString() {

return "User{" +

"username='" + username + '\'' +

", password='" + password + '\'' +

'}';

}

}

@RestController

@RequestMapping("/request_body")

public class RequestBodyController {

@PostMapping("/entity")

public String entity(@RequestBody User user) {

return user.toString();

}

}

2.使用 Map 接收

@PostMapping("/map")

public String map(@RequestBody Map map) {

return map.toString();

}

3.自定义实体类 (复杂对象)

@Data

@NoArgsConstructor

@AllArgsConstructor

public class Complex {

private String tag;

private List list;

private User[] array;

private Map map;

}

@PostMapping("/complex")

public String complex(@RequestBody Complex complex) {

return complex.toString();

}

请求参数

{

"tag": "complex",

"list": [

{

"username": "anna",

"password": "123"

},

{

"username": "bob",

"password": "456"

}

],

"array": [

{

"username": "cindy",

"password": "135"

},

{

"username": "david",

"password": "246"

}

],

"map": {

"u1": {

"username": "eric",

"password": "159"

},

"u2": {

"username": "frank",

"password": "357"

}

}

}

4.Content-Type 为 application/xml

添加 POM 文件

com.fasterxml.jackson.dataformat

jackson-dataformat-xml

2.16.1

接口方法 (和案例1只有方法名不一样)

@PostMapping("/xml")

public String xml(@RequestBody User user) {

return user.toString();

}

请求头设置

将 Content-Type 设置为 application/xml (一般情况下 Content-Type 为 application/json)

传递参数

tom

123

响应结果

源码解析

InvocableHandlerMethod#getMethodArgumentValues

参数的处理分为两个阶段:

判断当前环境中存在的resolvers,是否支持解析当前参数处理参数判断是否支持解析当前参数

我的环境中存在27个resolvers,通过命名我们大概可以猜测出 RequestResponseBodyMethodProcessor 是处理 @RequestBody 注解的 resolver

RequestResponseBodyMethodProcessor#supportsParameter

即如果参数上存在 @RequestBody 注解,则使用 RequestResponseBodyMethodProcessor 处理参数

RequestResponseBodyMethodProcessor#resolveArgument

RequestResponseBodyMethodProcessor 的 resolveArgument 方法会遍历当前环境中的 HttpMessageConverters,如果存在一个 HttpMessageConverter 的 canRead 方法返回 true,则使用该 HttpMessageConverter 的 read 方法读取数据。

案例1、2、3 的原理其实是一样的,request 的 Content-Type 为 application/json,spring-boot-starter-web 会引入 json 相关依赖,所以默认情况下,SpringMVC 有把 json 对象转换成 key-value 形式数据结构的能力

案例4 我们引用了相关依赖,使得 SpringMVC 有把 xml 对象转换成 key-value 形式数据结构的能力

小结

如果 request 的 Content-Type 为 xxx,并且存在一个 HttpMessageConverter 支持解析 Content-Type 为 xxx 的数据,通过 @RequestBody 注解,就可以将数据绑定到 key-value 形式的数据结构上

扩展:自定义HttpMessageConverter,处理指定 Content-Type 的请求

创建HttpMessageConverter

public class UserTextPlainHttpMessageConverter implements HttpMessageConverter {

@Override

public boolean canRead(Class clazz, MediaType mediaType) {

return clazz == User.class && "text".equals(mediaType.getType()) && "plain".equals(mediaType.getSubtype());

}

@Override

public boolean canWrite(Class clazz, MediaType mediaType) {

return false;

}

@Override

public List getSupportedMediaTypes() {

return Collections.singletonList(MediaType.TEXT_PLAIN);

}

@Override

public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {

try (InputStream inputStream = inputMessage.getBody();

BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {

String line;

StringBuilder sb = new StringBuilder();

while ((line = reader.readLine()) != null) {

sb.append(line);

}

ObjectMapper objectMapper = new ObjectMapper();

return objectMapper.readValue(sb.toString(), User.class);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

@Override

public void write(Object o, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

}

}

创建配置类

@Configuration

public class MessageConfig implements WebMvcConfigurer {

@Override

public void configureMessageConverters(List> converters) {

converters.add(new UserTextPlainHttpMessageConverter());

}

}

接口方法 (和案例1、4 只有方法名不一样)

@PostMapping("/convert")

public String convert(@RequestBody User user) {

return user.toString();

}

Postman设置

Content-Type 设置为 text/plain

body 传参类型为 text

接口响应

相关推荐

槐花怎么储存最好(槐花怎么长期储存)
怎么无限注册365游戏账号

槐花怎么储存最好(槐花怎么长期储存)

🕒 07-20 👀 803
缱绻造句
怎么无限注册365游戏账号

缱绻造句

🕒 06-27 👀 1205