突发奇想,在SpringBoot中,Controller的参数中有Map接口类型的
@GetMapping("/common")
public HttpResult testCommonGet(Map<String,String> map){
return HttpResult.success("common测试get");
}
像这里的map,声明是Map接口,那他的实现类是什么?
如果改为HashMap类型,有什么优缺点吗?
突发奇想,在SpringBoot中,Controller的参数中有Map接口类型的
@GetMapping("/common")
public HttpResult testCommonGet(Map<String,String> map){
return HttpResult.success("common测试get");
}
像这里的map,声明是Map接口,那他的实现类是什么?
如果改为HashMap类型,有什么优缺点吗?
先说结论,分两种情况。
情况1:不带注解的 Controller 方法 Map 参数类型,Map 的实现类是 Spring 提供的 BindingAwareModelMap,HashMap 完全可以替换 Map。需要注意的是此时 Map 不是用来接收请求参数的,而是作为 view 中使用的一个 model,用来填充 view ,如将 jsp 页面中的变量替换为具体的值。
情况2:带 @RequestBody 注解将 Map 作为请求体,Map 的实现类是 LinkedHashMap,HashMap 可以替换 Map。
情况1分析:Spring Controller 中的方法参数类型多种多样,Spring 调用 Controller 方法时必须保证能够为每个参数提供对应的值,Spring 的解决方案是提供一个通用的接口 HandlerMethodArgumentResolver,不同的参数类型由不同的实现处理。
HandlerMethodArgumentResolver 接口定义如下。
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
如果 HandlerMethodArgumentResolver 的实现支持给定的参数类型,就会将参数类型解析为值,Spring 解析不带注解的 Map 参数类型使用的是 MapMethodProcessor。源码如下。
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
public MapMethodProcessor() {
}
public boolean supportsParameter(MethodParameter parameter) {
return Map.class.isAssignableFrom(parameter.getParameterType()) && parameter.getParameterAnnotations().length == 0;
}
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
return mavContainer.getModel();
}
public boolean supportsReturnType(MethodParameter returnType) {
return Map.class.isAssignableFrom(returnType.getParameterType());
}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof Map) {
mavContainer.addAllAttributes((Map)returnValue);
} else if (returnValue != null) {
throw new UnsupportedOperationException("Unexpected return type [" + returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
}
}
}
可以看到,只要是 Map 类型参数,Spring 就会调用 ModelAndViewContainer#getModel
获取值,相关源码如下。
public class ModelAndViewContainer {
private final ModelMap defaultModel = new BindingAwareModelMap();
public ModelMap getModel() {
if (this.useDefaultModel()) {
return this.defaultModel;
} else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
}
最终使用的就是 ModelAndViewContainer 内部的 BindingAwareModelMap 实例,这个类实现了 LinkedHashMap,这也意味着如果你使用 TreeMap 作为参数,由于类型不匹配将会导致程序报错。
情况2分析:带 @RequestBody 的参数,由 RequestResponseBodyMethodProcessor 解析,默认情况下交由 MappingJackson2HttpMessageConverter 实例化参数,如果是 Map 实例化出来的是 HashMap 类型,这块源码内容较多,可自己尝试分析。
最后如果你对 Spring 实现感兴趣,也可以看我的博客或者和我私聊讨论,有帮助麻烦采纳哦!