此前一直以为下面两种声明请求参数的方式是异曲同工的,起到同样的效果
@GetMapping(value = "/list",params = {"ids"})
public void selByIds(List<String> ids) {
return;
}
@GetMapping(value = "/list")
public void selByIds(@RequestParam(value="ids") List<String> ids) {
return;
}
实际上,在请求参数为List时,我发现了一些问题。
如果在@GetMapping中声明请求参数,后端会报找不到java.util.List构造器的错误。
原来后端拿到前端请求后,会去寻找List的构造方法,试图为List创建实例。但List是什么?一个接口,自然找不到构造方法。
使用@RequestParam则传值正常。
开始看源码
发现两者进行参数解析时,使用的解析器HandlerMethodArgumentResolver是不同的
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
@RequestParam用的是RequestParamMethodArgumentResolver。
@GetMapping方式,用的是ServletModelAttributeMethodProcessor。
RequestParamMethodArgumentResolver在其父类AbstractNamedValueMethodArgumentResolver的方法resolveArgument中使用WebDataBinder将原本的Object参数转成了ArrayList对象。所以才能成功被List参数接收
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
ServletModelAttributeMethodProcessor则对List这样的引用类型的参数,使用构造器创建实例。
为什么两种方式使用的参数Resolver不一样?
其实是在HandlerMethodArgumentResolverComposite这个参数解析委托类中存着一个名为argumentResolverCache的Map,在Springboot容器初始化时就初始化好了。每个Controller方法的参数情况都对应一个MethodParameter$HandlerMethodParmeter。每次要对某方法参数进行解析时,先先使对应的HandlerMethodParmeter找到在该解析器Map中找到对应的解析器,然后进行解析。
最近比较忙,先到这,不去深究Springboot处理@RequestParam和@GetMapping的详细过程。
总之,知道它两声明请求参数时,对参数的解析方式是不一样的,之后有时间再来填坑。