spring mvc中@RequestBody注解的作用说明

 更新时间:2022年8月18日 19:20  点击:429 作者:健康平安的活着

@RequestBody的作用

@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的),所以只能发送POST请求。

GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。

在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。

jQuery中,$.ajax()默认发送的参数类型及编码为:application/x-www-form-urlcoded,而@RequestBody处理的参数类型及编码为:aplication/json或者是application/xml,通过contentType属性来指定

在传递之前,对JSON对象要使用JSON.stringify(),JSON.stringify() 方法将一个 JavaScript 值(对象或者数组)转换为一个 JSON 字符串

后端@RequestBody注解对应的类在将HTTP的输入流(含请求体)装配到目标类(即:@RequestBody后面的类)时,会根据json字符串中的key来匹配对应实体类的属性,如果匹配一致且json中的该key对应的值符合(或可转换为)实体类的对应属性的类型要求时,会调用实体类的setter方法将值赋给该属性。

注:当同时使用@RequestParam()和@RequestBody时,@RequestParam()指定的参数可以是普通元素、数组、集合、对象等等

(即:当,@RequestBody 与@RequestParam()可以同时使用时,原SpringMVC接收参数的机制不变,只不过RequestBody 接收的是请求体里面的数据;而RequestParam接收的是key-value里面的参数,所以它会被切面进行处理从而可以用普通元素、数组、集合、对象等接收)

实现案例

1. jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
    <script>
        var userList=new Array();
        userList.push({userName:"beijing",age:34});
        userList.push({userName:"shanghai",age:89});
        $.ajax({
            type:"post",
            url:"${pageContext.request.contextPath}/bike7",
            data:JSON.stringify(userList),
            contentType:"application/json;charset=utf-8",
            success:function(result){
                alert(result);
            }
        })
    </script>
</head>
<body>
 
</body>
</html>

2. controller

  /**
     * @author liujianfu
     * @description      controller中业务方法的集合参数获取,要将集合参数封装到一个pojo中才可以
     * 参数属性名与请求参数名称一致,参数值会自动映射匹配
     * @date 2021/1/10 22:14
     * @param
     * @return
     */
    @RequestMapping("/bike6")
    public String bike6(){
      System.out.println("controller6:");
        return "ajax";
    }
    /**
     * @author liujianfu
     * @description      controller中业务方法的集合参数获取,要将集合参数封装到一个pojo中才可以
     * 参数属性名与请求参数名称一致,参数值会自动映射匹配
     * @date 2021/1/10 22:14
     * @param
     * @return
     */
    @RequestMapping("/bike7")
    @ResponseBody
    public String bike7(@RequestBody List<User> userList){
        for(User u:userList){
            System.out.println("user:"+u.getUserName());
        }
        return "ok";
    }

3. spring-mvc 配置文件

4. 页面访问

@RequestBody原理

通过Http传递参数一般有两种方式,一种是通过url解析参数,一种是通过body来解决,那么我们本次说的RequestBody就是去解析请求体然后映射到我们的参数,那它该如何解析body呢?这就是本篇博客诞生的目的。

这个其实是SpringMVC中做的一个处理机制,在整个SpringMVC的处理流程中,会通过HandlerMethod来代理每个Map后的controller和method,在通过反射invoke method的过程中,会解析request来获得arguments,而@RequestBody就是在解析参数的这个过程中起作用的

1. 执行流程

在InvocableHandlerMethod#getMethodArgumentValues中,它会遍历当前HandlerMethod的参数,对于每一个参数,它都会通过HandlerMethodArgumentResolverComposite#supportsParameter来判断是否可以被解析器解析,如果可以被解析,则通过HandlerMethodArgumentResolverComposite#resolveArguement进行解析

对于@RequestBody来说,它对应的解析器是RequestResponseBodyMethodProcessor,那么,我们就深入到它的源码中来一探究竟:

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
	// 是否可以解析当前参数
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}
    // 是否可以解析当前返回值
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}
	
	@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		parameter = parameter.nestedIfOptional();
        // 解析handlerMethod中的参数
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        // 获取变量名
		String name = Conventions.getVariableNameForParameter(parameter);
		if (binderFactory != null) {
			WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
			if (arg != null) {
                // 通过binder校验@Validated注解的字段
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
				}
			}
			if (mavContainer != null) {
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
			}
		}
        // 如果方法是Optional参数,则代理
		return adaptArgumentIfNecessary(arg, parameter);
	}
	@Override
	protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
			Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		Assert.state(servletRequest != null, "No HttpServletRequest");
		ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
        // 解析http请求中的body并映射到对应的parameter上
		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
		if (arg == null && checkRequired(parameter)) {
			throw new HttpMessageNotReadableException("Required request body is missing: " +
					parameter.getExecutable().toGenericString());
		}
		return arg;
	}
	protected boolean checkRequired(MethodParameter parameter) {
		RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
		return (requestBody != null && requestBody.required() && !parameter.isOptional());
	}
}

2. 注册流程

那么RequestResponseBodyMethodProcessor是如何被注册到resolver中呢?主要是在RequestMappingHandlerAdapter中:

  • RequestMappingHandlerAdapter实现了HandlerAdapter这个接口,这个接口是MVC框架的SPI,DispatcherServlet通过此接口访问所有已安装的处理程序。
  • HandlerAdapter主要是路由之后方法的适配器,DispatcherServlet在路由之后通过HandlerAdapter来执行真实的操作(handlerAdapter是通过HandlerMethod来执行的)

对于RequestResponseBodyMethodProcessor来说,它实现了InitializingBean,在bean初始化之后会添加参数处理器和返回值处理器,对于参数处理器来说,内容如下:

从图中我们可以看到,RequestMappingHandlerAdapter在初始化的时候会把系统给定的,自定义的参数解析器加载到内存中。

加入说,我们要自定义一个参数解析器,系统会在什么时候加载进入内存呢?

我们发现RequestMappingHandlerAdapter#setCustomArgumentResolvers这个方法就是要去设置自定义参数解析器的,那么我们只需要找到它的调用方即可。

我们只需要实现WebMvcConfigurer即可(这点关系到Spring的自动装配,暂时没有看到,先鸽一下)

3. 设计优点

设计模式

采用策略模式+工厂模式 + 组合模式:

对于HandlerMethod的参数和返回值处理来说,对应着不同的处理方式,即对应着不同的策略,所以此处用的策略模式来处理的。至于HandlerMethodArgumentResolverComposite它则对应着策略工厂,同时,因为这个类实现了HandlerMethodArgumentResolver,所以它也是组合模式的变形,具体的策略类是HandlerMethodArgumentResolver

类图如下:

缓存处理

在参数解析工厂中,刚开始的参数解析器是在刚启动时注册到list中,但是如果之后被使用的时候就会存放到map中,可以直接获得(key是MethodParam),提高路由效率

附:常见的MVC参数注解

对于url解析参数来说,有两个注解,分别是pathVariable(指一种占位符)和requestParam,对于body来说,有requestBody。不加注解,也可以直接把url转为对应参数或者实体类

1.@PathVariable: www.666.com/web/6

@GetMapping("/web/{node}")
public ReturnType listEmployeeInNode(@PathVariable String node) throws BusinessException {
}

2.@ReqeustParam: www.666.com/web?user=1

@GetMapping("/web")
public ReturnType listEmployeeInNode(@RequestParam("user") String node) throws BusinessException {
}

3.@RequestBody: www.666.com/web body中是json

@GetMapping("/web")
public ReturnType listEmployeeInNode(@RequestBody UserDTO userDto) throws BusinessException {
}

4.无注解:www.666.com/web?userId=1&pwd=2

@PostMapping("/web")
public ReturnType listEmployeeInNode(UserDTO userDto) throws BusinessException {
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。

原文出处:https://blog.csdn.net/u011066470/article/details/112502544

[!--infotagslink--]

相关文章

  • Spring AOP 对象内部方法间的嵌套调用方式

    这篇文章主要介绍了Spring AOP 对象内部方法间的嵌套调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-29
  • Spring Cloud 中@FeignClient注解中的contextId属性详解

    这篇文章主要介绍了Spring Cloud 中@FeignClient注解中的contextId属性详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-25
  • Springboot如何实现Web系统License授权认证

    这篇文章主要介绍了Springboot如何实现Web系统License授权认证,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-05-28
  • Swagger中@ApiIgnore注解的使用详解

    这篇文章主要介绍了Swagger中@ApiIgnore注解的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-21
  • 校验非空的注解@NotNull如何取得自定义的message

    这篇文章主要介绍了校验非空的注解@NotNull如何取得自定义的message,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-29
  • 如何在Spring WebFlux的任何地方获取Request对象

    这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26
  • 详解SpringCloudGateway内存泄漏问题

    这篇文章主要介绍了详解SpringCloudGateway内存泄漏问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-16
  • Spring为什么不推荐使用@Autowired注解详析

    @Autowired 注解的主要功能就是完成自动注入,使用也非常简单,但这篇文章主要给大家介绍了关于Spring为什么不推荐使用@Autowired注解的相关资料,需要的朋友可以参考下...2021-11-03
  • Springboot如何使用mybatis实现拦截SQL分页

    这篇文章主要介绍了Springboot使用mybatis实现拦截SQL分页,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-19
  • Java中lombok的@Builder注解的解析与简单使用详解

    这篇文章主要介绍了Java中lombok的@Builder注解的解析与简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-06
  • 处理@PathVariable注解允许参数为空、允许不传参数的问题

    这篇文章主要介绍了处理@PathVariable注解允许参数为空、允许不传参数的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-23
  • SpringMVC文件上传原理及实现过程解析

    这篇文章主要介绍了SpringMVC文件上传原理及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-15
  • Mybatis用注解写in查询的实现

    这篇文章主要介绍了Mybatis用注解写in查询的实现方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-13
  • Spring Data JPA 关键字Exists的用法说明

    这篇文章主要介绍了Spring Data JPA 关键字Exists的用法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-10
  • C# MVC模式中应该怎样区分应用程序逻辑(Controller层)和业务逻辑(Model层)?

    这篇文章主要介绍了C# MVC模式中应该怎样区分应用程序逻辑(Controller层)和业务逻辑(Model层)?,这也小编做.NET项目时经常思考和让人混乱的一个问题,这篇文章写的挺好,一下清晰了许多,需要的朋友可以参考下...2020-06-25
  • 解决@Transactional注解事务不回滚不起作用的问题

    这篇文章主要介绍了解决@Transactional注解事务不回滚不起作用的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-23
  • tomcat启动完成执行 某个方法 定时任务(Spring)操作

    这篇文章主要介绍了tomcat启动完成执行 某个方法 定时任务(Spring)操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-25
  • 使用Maven 搭建 Spring MVC 本地部署Tomcat的详细教程

    这篇文章主要介绍了使用Maven 搭建 Spring MVC 本地部署Tomcat,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-08-16
  • Java Spring Cloud 负载均衡详解

    这篇文章主要介绍了Spring Cloud负载均衡及远程调用实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2021-09-18
  • SpringMvc自动装箱及GET请求参数原理解析

    这篇文章主要介绍了SpringMvc自动装箱及GET请求参数原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-19