首先定义两个参数类
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();
}
请求体
控制台
看到将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的功能
上面说的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~~