首先定义两个参数类

1、TestDTO

package com.xx.log.common.pojo.dto;

import com.xx.log.common.pojo.group.GroupV1;
import com.xx.log.common.pojo.group.GroupV2;
import lombok.Data;
import lombok.ToString;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Data
@ToString
public class TestDTO {

    @NotBlank(message = "info 不能为空")
    @NotNull(message = "info 不能为null")
    @Size(min = 1, max = 4, groups = GroupV1.class)
    @Size(min = 6, max = 10, groups = GroupV2.class)
    private String info;

    private String usp;
}

2、Test2DTO

package com.xx.log.common.pojo.dto;

import com.xx.log.common.pojo.group.GroupV1;
import com.xx.log.common.pojo.group.GroupV2;
import lombok.Data;
import lombok.ToString;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Data
@ToString
public class Test2DTO {

    @NotBlank(message = "info 不能为空")
    @NotNull(message = "info 不能为null")
    @Size(min = 1, max = 4, groups = GroupV1.class)
    @Size(min = 6, max = 10, groups = GroupV2.class)
    private String info;

    private String usp;
    private String ip;
}
GET请求

1、controller接收多个参数情况:
直接使用实体作为controller参数,spring会自动处理

@RequestMapping("/test-get")
    public void testGet(TestDTO testDTO, Test2DTO test2DTO) {
        log.info("testDTO:{} test2DTO:{}", testDTO, test2DTO);
    }

请求地址

http://localhost:10086/test-get?info=tt&ip=kk

打印结果

testDTO:TestDTO(info=tt, usp=null) test2DTO:Test2DTO(info=tt, usp=null, ip=kk)

从结果上看,两个参数都有了值的映射

2、使用 @ModelAttribute 注解

public void testGet(@ModelAttribute TestDTO testDTO, @ModelAttribute Test2DTO test2DTO) {
        log.info("testDTO:{} test2DTO:{}", testDTO, test2DTO);
    }

效果跟第一种方式是一致的

POST请求

post方式和get方式是有区别的,controller如下:

@PostMapping("/test2")
    public TestVO test2(@RequestBody TestDTO testDTO, @RequestBody Test2DTO test2DTO) {
        log.info("testDTO:{} test2DTO:{}", testDTO, test2DTO);
        return TestVO.builder().code(0).msg(MixUtil.toJsonString(testDTO)).build();
    }

请求体

springboot一个post请求接收多个参数 springboot接收多个对象_spring


控制台

springboot一个post请求接收多个参数 springboot接收多个对象_spring_02


看到将requestbody里的值映射到两个参数会报错的,主因就是框架处理带有@RequestBody这种注解的参数时会主动从输入流中读取一次数据,当读取之后会关闭流;第二个参数读取时流已经关闭,所以会抛异常;

通过实验,第二个参数不加 @RequestBody 不会报错,但也获取不到页面传过来的值

所以在设计的时候,设计成一个对象接收 前端请求 是正确的;

spring mvc 除了上面讲的参数映射还有多种其他方式获取参数方式,这里省略了

那如果我硬要把post的controller方法设计成两个参数可以吗(面试的时候会这样问)?答案是可以的,需要借助HandlerMethodArgumentResolver

HandlerMethodArgumentResolver
package org.springframework.messaging.handler.invocation;

import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;

/**
 * Strategy interface for resolving method parameters into argument values
 * in the context of a given {@link Message}.
 *
 * @author Rossen Stoyanchev
 * @since 4.0
 */
public interface HandlerMethodArgumentResolver {

	boolean supportsParameter(MethodParameter parameter);

	@Nullable
	Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception;
}

场景:我们的post方法第一个参数(@RequestBody注解标记)接收前端请求,
第二个参数我们想接收HttpServletRequest 对象,还比如第二个参数我们想用redis通过一个key去拿到值,这个时候就需要HandlerMethodArgumentResolver了,这个对象可以根据我们自定义逻辑识别我们需要的类,进而构建类进行注入。

Spring web 的 HandlerMethodArgumentResolver默认提供有如下若干个实现,这是我们能在controller方法里直接使用其支持对象的原因,我们用的比较多的比如 HttpServletRequest,HttpServletResponse就是下面两个Resolver的功能

springboot一个post请求接收多个参数 springboot接收多个对象_java_03

上面说的redis场景我们可以自定义扩展HandlerMethodArgumentResolver即可:

1、自定义一个Redis注解

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Redis {
    
    /**
     * redis中的Key
     */
    String key();    
}

2、扩展 HandlerMethodArgumentResolver

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.util.Random;

/**
 * 从redis中获取值放入到参数中
 *
 */
public class RedisMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
    private static final Logger log = LoggerFactory.getLogger(RedisMethodArgumentResolver.class);
    
    /**
     * 处理参数上存在@Redis注解的
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(Redis.class);
    }
    
    /**
     * 解析参数  伪代码
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {

        // 获取到这个注解
        Redis redis = parameter.getParameterAnnotation(Redis.class);
        // 获取在redis中的key       
        // 从redis中获取值       
        // 返回值
        return “xxxx”;
    }
}

3、注册 RedisMethodArgumentResolver

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    /**
     * 这个地方加载的顺序是在默认的HandlerMethodArgumentResolver之后的
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new RedisMethodArgumentResolver());
    }
}

4、在controller里使用 @Redis 注解参数即可

@GetMapping("redisArgumentResolver")
    public void redisArgumentResolver(@RequestParam("hello") String hello,
                                      @Redis(key = "redisKey") String redisValue) {
        log.info("控制层获取到的参数值: hello:[{}],redisValue:[{}]", hello, redisValue);
    }

over~~