Spring MVC Controller 方法参数 Map 的实现类是什么?

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 问题题主问题描述如下:在SpringBoot中,Controller的参数中有Map接口类型的,请问他的实现类是什么?突发奇想,在SpringBoot中,Controller的参数中有Map接口类型的

问题


题主问题描述如下:

在SpringBoot中,Controller的参数中有Map接口类型的,请问他的实现类是什么?

突发奇想,在SpringBoot中,Controller的参数中有Map接口类型的

    @GetMapping("/common")
    public HttpResult testCommonGet(Map<String,String> map){
        return  HttpResult.success("common测试get");
    }


像这里的map,声明是Map接口,那他的实现类是什么?

如果改为HashMap类型,有什么优缺点吗?


分析


题主提到 SpringBoot 中的 Controller 参数,可以推测题主在学习 Spring 框架时跳过了 Spring MVC 直接上手的 Spring Boot。自从 Spring Boot 框架推出之后,确实简化了 Spring 的使用,这也让很多人忽略了框架底层的实现。


Spring Boot 基于 Spring Framework,Spring MVC 正是 Spring Framework 框架中的其中一个模块。Controller 并非 Spring Boot 的专利,要回答题主的问题,还是需要回归到 Spring MVC,Spring MVC 环境已经支持了 Controller 方法使用不同的参数类型。


Spring MVC 中的 Controller 方法参数多用来接收请求有关的信息,还有一部分和 MVC 有关,能定义的请求相关参数可以参见《深入理解 Spring MVC Controller —— 请求参数获取》。


对于不同类型参数的解析,Spring MVC 使用的是 HandlerMethodArgumentResolver,默认情况下的实现及能处理的参数见下图。


36.png


回答


因此具体到 Controller 方法的 Map 类型的参数实现,要看 HandlerMethodArgumentResolver 如何解析的,默认情况下解析 Map 类型的 HandlerMethodArgumentResolver 主要分下面几种情况。


1. 携带指定了 name 的 @RequestParam 注解的 Map 类型


这种情况下的参数由 RequestParamMethodArgumentResolver 进行解析,不过由于默认情况下 Spring 无法将 String 转换为 Map,因此会报错。如果添加了支持 String 到 Map 类型转换服务,则 Map 实现由具体的类型转换服务决定。


{
    "timestamp": "2022-02-13T05:18:01.147+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Failed to convert value of type 'java.lang.String' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map': no matching editors or conversion strategy found",
    "path": "/test"
}


2. 携带未指定 name 的 @RequestParam 注解的 Map 类型参数


这种情况下的参数由 RequestParamMapMethodArgumentResolver 进行解析,支持的 Map 泛型类型包括 Map、Map、Map,不管哪种泛型类型,Map 参数的实现类型都为 LinkedHashMap,相关解析代码如下。


37.png


3. 携带指定了 name 的 @PathVariable 注解的 Map 类型参数


这种类型的 Map 参数由 PathVariableMethodArgumentResolver 进行解析,由于默认情况下 Spring 无法将路径中的字符串变量转换为 Map,因此同样会报错。如果添加了支持 String 到 Map 类型转换服务,则 Map 实现由具体的类型转换服务决定。


{
    "timestamp": "2022-02-13T05:36:31.518+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Failed to convert value of type 'java.lang.String' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map': no matching editors or conversion strategy found",
    "path": "/a=1"
}


4. 未携带 name 的 @PathVariable 注解的 Map 类型参数


这种情况下的 Map 参数由 PathVariableMapMethodArgumentResolver 进行解析,Spring 使用的实现同样为 LinkedHashMap,相关代码如下。


38.png


5. 携带指定了 name 的 @MatrixVariable 注解的 Map 类型参数


这种情况的 Map 参数由 PathVariableMapMethodArgumentResolver 进行解析,由于默认情况下 Spring 无法将路径中的字符串变量转换为 Map,因此同样会报错。如果添加了支持 String 到 Map 类型转换服务,则 Map 实现由具体的类型转换服务决定。


{
    "timestamp": "2022-02-14T02:02:53.772+0000",
    "status": 500,
    "error": "Internal Server Error",
    "message": "Failed to convert value of type 'java.lang.String' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map': no matching editors or conversion strategy found",
    "path": "/aaa/param=1;bbb"
}


6. 未携带 name 的 @MatrixVariable 注解的 Map 类型参数


这种情况下的 Map 参数由 MatrixVariableMapMethodArgumentResolver 解析,这种情况下的 Map 参数实现为 LinkedHashMap,相关源码如下。


39.png


7. 未携带任何注解的 Map 类型


不带注解的 Controller 方法中的 Map 参数被 Spring 认为是 MVC 中的 Model,由 MapMethodProcessor 进行解析,相关代码如下。


40.png


可以看到,Spring 使用的 Map 实现是 ModelAndViewContainer#getModel 方法的返回值,看下这个方法。


public class ModelAndViewContainer {
  private final ModelMap defaultModel = new BindingAwareModelMap();
  public ModelMap getModel() {
    if (useDefaultModel()) {
      // 没有重定向,使用默认 Model
      return this.defaultModel;
    } else {
        // 发生重定向,使用 ModelMap
      if (this.redirectModel == null) {
        this.redirectModel = new ModelMap();
      }
      return this.redirectModel;
    }
  }
  private boolean useDefaultModel() {
    return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
  }
}


如果没有发生重定向,Spring 使用的 Map 实现是 ModelAndViewContainer 内部的 BindingAwareModelMap 实例;

如果发生了重定向,Spring 使用的 Map 实现是 ModelMap;

BindingAwareModelMap 和 ModelMap 都实现了 LinkedHashMap,这也意味着如果你使用 TreeMap 作为参数,由于类型不匹配将会导致程序报错。


8. 携带 @RequestBody 注解的 Map 参数


带 @RequstBody 注解的 Map 参数由 RequestResponseBodyMethodProcessor 解析,默认情况下交由 MappingJackson2HttpMessageConverter 实例化参数,实例化出来的是 HashMap 类型,这块源码内容较多,可自己尝试分析。


总结

Spring MVC 对于 Map 类型的 Controller 方法参数,多数情况下使用 LinkedHashMap 作为实现,这样可以保证参数有序,兼容了程序获取指定索引位置参数的情况。


目录
相关文章
|
2月前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
181 73
|
3月前
|
存储
`map()`方法在什么场景下会比 `forEach()`方法更高效?
综上所述,当需要对数组元素进行复杂的转换并生成新数组、进行链式调用和函数式编程、处理元素之间存在明确映射关系的情况以及与其他数组方法结合使用时,`map()`方法比`forEach()`方法更高效,能够使代码更加简洁、清晰和易于维护。
83 32
WK
|
3月前
|
Python
Python中format_map()方法
在Python中,`format_map()`方法用于使用字典格式化字符串。它接受一个字典作为参数,用字典中的键值对替换字符串中的占位符。此方法适用于从字典动态获取值的场景,尤其在处理大量替换值时更为清晰和方便。
WK
142 36
|
3月前
|
前端开发 Java UED
SpringMVC全局异常处理+拦截器使用+参数校验
通过使用 SpringMVC 的全局异常处理、拦截器和参数校验,可以有效提升 Web 应用程序的安全性、稳定性和用户体验。这些技术的合理应用,不仅可以保证代码的健壮性,还能提高代码的可维护性,为开发高质量的 Web 应用程序提供了坚实的基础。
77 6
|
3月前
|
存储 JavaScript 前端开发
如何选择使用`map()`方法和`forEach()`方法?
选择使用`map()`方法还是`forEach()`方法主要取决于操作的目的、是否需要返回值、代码的可读性和维护性等因素。在实际开发中,需要根据具体的业务需求和场景来灵活选择合适的方法,以实现更高效、更易读和更易维护的代码。
45 3
|
3月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
55 1
|
3月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
53 1
|
3月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
48 0
|
XML 缓存 Java
模仿Spring实现一个类管理容器
项目的初衷是独立作出一个成熟的有特色的IOC容器,但由于过程参考Spring太多,而且也无法作出太多改进,于是目的变为以此项目作为理解Spring的一个跳板,与网上的一些模仿Spring的框架不同,本项目主要是针对注解形式
544 0
模仿Spring实现一个类管理容器
|
27天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
201 17
Spring Boot 两种部署到服务器的方式