背景前段时间开发一个接口,因为调用我接口的同事脾气特别好,我也就不客气,我就直接把源代码发给他当接口定义了。

没想到同事看到我的代码问:要么 get  a,b,c  要么  post [a,b,c]。这么写可以自动解析?他们一直都是自己转换成list。

我很肯定的说可以,但是已经习惯这么用了,没有了解底层的机制,这里其实RequestParam这个注解是不能省略的,普通的字符串参数可以自动绑定,需要这种内部转换的不可以。参数绑定原理Spring的参数解析使用HandlerMethodArgmentResolver类型的组件完成。不同类型的使用不同的ArgumentResolver来解析。具体参考RequestMappingHandlerAdapter类的源码。里面有个方法是很好的诠释:

// 获取默认的 HandlerMethodArgumentResolverprivate ListgetDefaultArgumentResolvers() {     List resolvers = new ArrayList();    // 1.基于注解的参数解析     // Annotation-based argument resolution    // 解析被注解 @RequestParam, @RequestPart 修饰的参数, 数据的获取通过 HttpServletRequest.getParameterValues    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));    // 解析被注解 @RequestParam 修饰, 且类型是 Map 的参数, 数据的获取通过 HttpServletRequest.getParameterMap    resolvers.add(new RequestParamMapMethodArgumentResolver());    // 解析被注解 @PathVariable 修饰, 数据的获取通过 uriTemplateVars, 而 uriTemplateVars 却是通过 RequestMappingInfoHandlerMapping.handleMatch 生成, 其实就是 uri 中映射出的 key  value    resolvers.add(new PathVariableMethodArgumentResolver());    // 解析被注解 @PathVariable 修饰 且数据类型是 Map, 数据的获取通过 uriTemplateVars, 而 uriTemplateVars 却是通过 RequestMappingInfoHandlerMapping.handleMatch 生成, 其实就是 uri 中映射出的 key  value    resolvers.add(new PathVariableMapMethodArgumentResolver());    // 解析被注解 @MatrixVariable 修饰, 数据的获取通过 URI提取了;后存储的 uri template 变量值    resolvers.add(new MatrixVariableMethodArgumentResolver());    // 解析被注解 @MatrixVariable 修饰 且数据类型是 Map, 数据的获取通过 URI提取了;后存储的 uri template 变量值    resolvers.add(new MatrixVariableMapMethodArgumentResolver());    // 解析被注解 @ModelAttribute 修饰, 且类型是 Map 的参数, 数据的获取通过 ModelAndViewContainer 获取, 通过 DataBinder 进行绑定    resolvers.add(new ServletModelAttributeMethodProcessor(false));    // 解析被注解 @RequestBody 修饰的参数, 以及被@ResponseBody修饰的返回值, 数据的获取通过 HttpServletRequest 获取, 根据 MediaType通过HttpMessageConverter转换成对应的格式, 在处理返回值时 也是通过 MediaType 选择合适HttpMessageConverter, 进行转换格式, 并输出    resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));    // 解析被注解 @RequestPart 修饰, 数据的获取通过 HttpServletRequest.getParts()    resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));    // 解析被注解 @RequestHeader 修饰, 数据的获取通过 HttpServletRequest.getHeaderValues()    resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));    // 解析被注解 @RequestHeader 修饰且参数类型是 Map, 数据的获取通过 HttpServletRequest.getHeaderValues()    resolvers.add(new RequestHeaderMapMethodArgumentResolver());    // 解析被注解 @CookieValue 修饰, 数据的获取通过 HttpServletRequest.getCookies()    resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));    // 解析被注解 @Value 修饰, 数据在这里没有解析    resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));    // 解析被注解 @SessionAttribute 修饰, 数据的获取通过 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_SESSION)    resolvers.add(new SessionAttributeMethodArgumentResolver());    // 解析被注解 @RequestAttribute 修饰, 数据的获取通过 HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_REQUEST)    resolvers.add(new RequestAttributeMethodArgumentResolver());    // 2.基于类型的参数解析器    // Type-based argument resolution    // 解析固定类型参数(比如: ServletRequest, HttpSession, InputStream 等), 参数的数据获取还是通过 HttpServletRequest    resolvers.add(new ServletRequestMethodArgumentResolver());    // 解析固定类型参数(比如: ServletResponse, OutputStream等), 参数的数据获取还是通过 HttpServletResponse    resolvers.add(new ServletResponseMethodArgumentResolver());    // 解析固定类型参数(比如: HttpEntity, RequestEntity 等), 参数的数据获取还是通过 HttpServletRequest    resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));    // 解析固定类型参数(比如: RedirectAttributes), 参数的数据获取还是通过 HttpServletResponse    resolvers.add(new RedirectAttributesMethodArgumentResolver());    // 解析固定类型参数(比如: Model等), 参数的数据获取通过 ModelAndViewContainer    resolvers.add(new ModelMethodProcessor());    // 解析固定类型参数(比如: Model等), 参数的数据获取通过 ModelAndViewContainer    resolvers.add(new MapMethodProcessor());    // 解析固定类型参数(比如: Errors), 参数的数据获取通过 ModelAndViewContainer    resolvers.add(new ErrorsMethodArgumentResolver());    // 解析固定类型参数(比如: SessionStatus), 参数的数据获取通过 ModelAndViewContainer    resolvers.add(new SessionStatusMethodArgumentResolver());    // 解析固定类型参数(比如: UriComponentsBuilder), 参数的数据获取通过 HttpServletRequest    resolvers.add(new UriComponentsBuilderMethodArgumentResolver());    // 3.自定义参数解析器    // Custom arguments    if (getCustomArgumentResolvers() != null) {        resolvers.addAll(getCustomArgumentResolvers());    }    // Catch-all    //这两个解析器可以解析所有类型的参数    resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));    resolvers.add(new ServletModelAttributeMethodProcessor(true));    return resolvers;}

在一步步跟踪源码之后,最终在PropertyEditorRegistrySupport这个类中,有一个createDefaultEditors的私有方法。里面定义了各种类型转换:

private void createDefaultEditors() {        this.defaultEditors = new HashMap(64);        this.defaultEditors.put(Charset.class, new CharsetEditor());        this.defaultEditors.put(Class.class, new ClassEditor());        this.defaultEditors.put(Class[].class, new ClassArrayEditor());        this.defaultEditors.put(Currency.class, new CurrencyEditor());        this.defaultEditors.put(File.class, new FileEditor());        this.defaultEditors.put(InputStream.class, new InputStreamEditor());        this.defaultEditors.put(InputSource.class, new InputSourceEditor());        this.defaultEditors.put(Locale.class, new LocaleEditor());        if(pathClass != null) {            this.defaultEditors.put(pathClass, new PathEditor());        }        this.defaultEditors.put(Pattern.class, new PatternEditor());        this.defaultEditors.put(Properties.class, new PropertiesEditor());        this.defaultEditors.put(Reader.class, new ReaderEditor());        this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());        this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());        this.defaultEditors.put(URI.class, new URIEditor());        this.defaultEditors.put(URL.class, new URLEditor());        this.defaultEditors.put(UUID.class, new UUIDEditor());        if(zoneIdClass != null) {            this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());        }        this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));        this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));        this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));        this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));        this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));        this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());        this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());        this.defaultEditors.put(Character.TYPE, new CharacterEditor(false));        this.defaultEditors.put(Character.class, new CharacterEditor(true));        this.defaultEditors.put(Boolean.TYPE, new CustomBooleanEditor(false));        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));        this.defaultEditors.put(Byte.TYPE, new CustomNumberEditor(Byte.class, false));        this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));        this.defaultEditors.put(Short.TYPE, new CustomNumberEditor(Short.class, false));        this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));        this.defaultEditors.put(Integer.TYPE, new CustomNumberEditor(Integer.class, false));        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));        this.defaultEditors.put(Long.TYPE, new CustomNumberEditor(Long.class, false));        this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));        this.defaultEditors.put(Float.TYPE, new CustomNumberEditor(Float.class, false));        this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));        this.defaultEditors.put(Double.TYPE, new CustomNumberEditor(Double.class, false));        this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));        this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));        this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));        if(this.configValueEditorsActive) {            StringArrayPropertyEditor sae = new StringArrayPropertyEditor();            this.defaultEditors.put(String[].class, sae);            this.defaultEditors.put(short[].class, sae);            this.defaultEditors.put(int[].class, sae);            this.defaultEditors.put(long[].class, sae);        }    }

从上面的方法里就可以知道都默认支持哪些类型的自动换换了。中间过程的源码不一一贴了。总结一下参数解析绑定的过程

1.SpringMVC初始化时,RequestMappingHanderAdapter类会把一些默认的参数解析器添加到argumentResolvers中。当SpringMVC接收到请求后首先根据url查找对应的HandlerMethod。

2.遍历HandlerMethod的MethodParameter数组。

3.根据MethodParameter的类型来查找确认使用哪个HandlerMethodArgumentResolver。

4.解析参数,从请求中解析出MethodParameter对应的参数,结果都是字符串。

5.转换参数,在DataBinder时PropertyEditorRegistrySupport把String转换成具体方法所需要的类型,这里就包括了基本类型、对象、List等。

相关阅读

SpringBoot优雅退出

SpringBoot整合web容器

你看不懂的spring原理是因为不知道这几个概念

专治不会看源码的毛病--spring源码解析AOP篇(2017版)

#{}不自动改参数类型_Spring参数的自解析还在自己转换?你out了!相关推荐

  1. html绑定带有形参的函数,Python中函数参数类型和参数绑定

    参数类型 Python函数的参数类型一共有五种,分别是: POSITIONAL_OR_KEYWORD(位置参数或关键字参数) VAR_POSITIONAL(可变参数) KEYWORD_ONLY(关键字 ...

  2. java 参数类型不确定_详细解析Java虚拟机的栈帧结构

    什么是栈帧? 正如大家所了解的,Java虚拟机的内存区域被划分为程序计数器.虚拟机栈.本地方法栈.堆和方法区.(什么?你还不知道,赶紧去看看<Java虚拟机内存结构及编码实战>)这次要介绍 ...

  3. C++代码片段(一)萃取函数返回值类型,参数类型,参数个数

    函数的类型主要集中在以下几种 函数指针 函数对象,是一个类对象,内部重载的operator()函数是一个函数指针 lambda,匿名函数对象,同函数对象 function对象 后三者都是类对象,可以看 ...

  4. python参数类型_Python 参数类型和参数匹配模型

    Python 方法的参数种类有很多,而不是通常语言定义的那样, Python 方法的传参能力要比想象的强大很多.很多初学者可能对一些库中带 * 带 ** 的参数类型非常奇怪,但是其实这些语法正是保证 ...

  5. gauge对应的JAVA类型_spring boot 源码解析38-GaugeService详解

    前言 本文来分析GaugeService的实现,其类图如下: 解析 GaugeService GaugeService–> 1个可以用来提交1个被命名的duble值为了存储和分析的服务.任意的统 ...

  6. C#的参数类型:params、out和ref

    PS:由于水平有限,难免会有错误和遗漏,欢迎各位看官批评和指正,谢谢~ 首先回顾一下C#声明一个方法的语法和各项元素,[]代表可选 [访问修饰符] 返回值 方法名([参数类型] 数据类型 参数名) { ...

  7. 【Kotlin】函数类型 ( 函数类型 | 带参数名称的参数列表 | 可空函数类型 | 复杂函数类型 | 带接收者函数类型 | 函数类型别名 | 函数类型实例化 | 函数调用 )

    文章目录 I . 函数类型 II . 带参数名的参数列表 III . 可空函数类型 IV . 复杂函数类型解读 V . 函数类型别名 VI . 带 接收者类型 的函数类型 VII . 函数类型实例化 ...

  8. SpringMVC的返回值和参数类型

    传统的基于Spring Framework的web开发需要大量的 xml 配置,在有SpringBoot以后,Web开发的效 率得到了很大的提升,几乎大部分配置可以使用默认约定的规则.我们基于Spri ...

  9. JVM参数类型大揭秘

    文章目录 JVM参数类型大揭秘 JVM的参数类型 标准参数 X参数 XX参数 -Xmx -Xms 运行时JVM参数查看 参数: jps jinfo jstat查看虚拟机统计信息 类装载 垃圾收集 JI ...

最新文章

  1. Android中Activity共享变量的另一方法:Application context
  2. python 最小硬币数_程序以找到要在Python中达到目标的硬币组合数量
  3. 专题 19 Makefile的使用
  4. Buffer Cache Hit Ratio
  5. C++知识回顾之__stdcall、__cdcel和__fastcall三者的区别
  6. mysql建立的一个自动更新组织树案案例
  7. QT的QUrl类的使用
  8. phpstudy mysql5.1_linux下mysql5.1 和 5.7安装教程详解
  9. 点击按钮弹出iframe_WEB安全(四) :CSRF与点击劫持
  10. XPDF3.04抽取PDF中的中文文本
  11. Vue中虚拟DOM的理解
  12. android编辑配置文件,如何在android studio中修改配置文件
  13. DataTable 中各种计算(笔记)
  14. 水晶报表合并模块部署指南(.Net2.0,VS2005)
  15. java---数组常用的方法:
  16. JDK各版本下载官网链接
  17. vue的UI框架之有赞移动端vant-ui
  18. autoit v3安装
  19. volatile的指令重排序理解
  20. vue 字典配置_vue 字典

热门文章

  1. python 中主线程结束 子线程还在运行么_「干货」python线程笔记
  2. ci如何使用中$.ajax 中的 url 如何使用php的代码,CI框架中使用ajax操作数据库有关问题...
  3. 事务注解放到类上面 下面私有方法有效吗_【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)...
  4. line java_java – Line Rasterization / 4-bresenham
  5. java 联合_如何在java中进行联合,相交,区分和反向数据
  6. Python安装cvxpy包的解决方案
  7. c语言 一个矩阵的乘积,c语言矩阵相乘
  8. JPA的单向一对多关联(oneToMany)实现示例(基于Spring Boot + JPA +MySQL,表自动维护)
  9. 一个简单简洁的社交媒体共享菜单
  10. JAVA泛型只能用引用类型_Java泛型和设计模式:不参数化对泛型类型的引用总是一件坏事吗?...