博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring参数的自解析--还在自己转换?你out了!
阅读量:5013 次
发布时间:2019-06-12

本文共 9360 字,大约阅读时间需要 31 分钟。

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

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

我很肯定的说可以,但是已经习惯这么用了,没有了解底层的机制,这里其实RequestParam这个注解是不能省略的,普通的字符串参数可以自动绑定,需要这种内部转换的不可以。

参数绑定原理
Spring的参数解析使用HandlerMethodArgmentResolver类型的组件完成。不同类型的使用不同的ArgumentResolver来解析。具体参考RequestMappingHandlerAdapter类的源码。里面有个方法是很好的诠释:

// 获取默认的 HandlerMethodArgumentResolverprivate List
getDefaultArgumentResolvers() { List
resolvers = new ArrayList
(); // 1.基于注解的参数解析 <-- 解析的数据来源主要是 HttpServletRequest | ModelAndViewContainer // 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等。

转载于:https://www.cnblogs.com/xiexj/p/11332525.html

你可能感兴趣的文章
JavaSE| String常用方法
查看>>
NRF51822配对绑定要点
查看>>
C语言博客作业—数据类型
查看>>
[leetcode]Count and Say
查看>>
cookie、session和token的概念入门
查看>>
保护网站页面内容+版权
查看>>
Golang模拟客户端POST表单功能文件上传
查看>>
重启进程
查看>>
js 进度条效果
查看>>
RelativeLayout
查看>>
distinct!!!!!!!!!!!!!!!!!!
查看>>
input type
查看>>
Javascript脚本 :Function 对象的定义和使用
查看>>
工作疑问之AWS ubuntu16.04 修改网卡名
查看>>
data:image/png;base64
查看>>
private关键字
查看>>
通过Performance Log确定磁盘有性能问题?
查看>>
form快速转json serialize
查看>>
POJ_1185_炮兵阵地 dp+状态压缩
查看>>
【笔记】Collection
查看>>