上回,我们大概讲了下HandlerAdapter。今天带大家来认识一下,我们最常用的RequestMappingHandlerAdapter。不过只能给大家先开个头,讲下参数解析。
在介绍HandlerAdapter时,我们就知道HandlerAdapter屏蔽了DispatcherServlet屏蔽了Handler的调用细节,也知道了@RequestMapping的中间桥梁是HandlerMethod。而这意味着RequestMappingHandlerMappping是负责调用HandlerMethod处理请求逻辑的。于是,我们很自然想到几个重要的问题:
@RequestMapping的参数来源
常见的几个相信很多同学都是知道:@RequestParam、@RequestBody、@PathVariable、@RequestHeader
还有些可能不太常用的:@SessiontAttribuite、@CookieValue、@MatrixVariable
HttpServletRequest、HttpServletResponse、HttpSession、Locale
这里就不再一一列举了,感兴趣的同学可以通过小标题的链接到官网看看。
参数解析器:HandlerMethodArgumentResolver
Spring抽象出来了参数解析器,用于解析不同的参数。也正是因为有各种不同来源的参数,所以Spring实现了多达31个参数解析器,且默认注册的多达25个参数解析器。

图示就是RequestMappingAdapterHandler默认注册的参数解析器,而HandlerMethodArgumentResolverComposite是一个特殊的实现。如果从策略模式的角度看,他就是策略选择器。用于寻找可以解析特定参数的参数解析器,并解析参数。
底层的支持体系
我们知道不同的参数解析器,负责不同来源的参数解析。但也因此需要不同的技术手段的支撑。其中,下面的最为重要:
类型转换体系:ConversionService
类型转换服务是spring-core中提供的,用于类型转换。而在SpringMVC中,只要是name-value这种类型的参数,则都需要依赖他来进行参数转换。例如,上面提到@RequestParam,将String转为Date。
注意,他背后是一个体系,涉及到一系列的相关接口,例如:Converter。这里不拓展了。
对象属性编辑体系:ConfigurablePropertyAccessor
ConfigurablePropertyAccessor是spring-beans提供的,用于修改对象属性。最典型的实现就是BeanWrapperImpl。同时他还可以在修改属性值之前进行参数转换。而这个参数类型转换能力,来自于两部分,一个是上面说的ConversionService。另一个是PropertyEditor,通常是我们自定义的。因为大部分的类型转换ConversionService都能做。最后有PropertyHandler通过反射将属性值设置进去。
对象属性绑定: DataBinder
DataBinder来自于spring-context,主要是利用上述的对象编辑体系来完成属性设置。而其子类WebDataBinder来自于spring-web,利用request作为属性值的来源完成请求入参的属性设置。除此之外,他还能做入参校验。我们的@Valid/@Validated就是他处理的。
Http消息转换器:HttpMessageConverter
Http消息转换器是spring-web的内容,主要有两个作用:
| 作用 |
|---|
| 将Request中的body参数反序列为目标类型的对象 |
| 将需要返回的响应值序列化为二进制流,通过Response写回响应数据 |
关于类型转换、DataBinder,感兴趣的同学可自行通过官网学习:
3. Validation, Data Binding, and Type Conversion
再回头看类的UML图,我们会发现参数解析器的接口方法中有一个WebDataBinderFactory参数。
到这里可能有些同学要晕了,一下子WebDataBinder,一下子又是HttpMessageConverter。又说不是所有的参数解析器都需要这些东西。就说咱最常用的两个参数解析器来说吧
| 参数解析器 | 描述 |
|---|---|
| RequestResponseBodyMethodProcessor | 从request.body中读取流,并利用HttpMessageConverter转换成目标参数对象。同时还利用WebDataBinder做入参校验 |
| RequestParamMethodArgumentResolver | 从地址栏获取参数,并利用WebDataBinder做参数类型转换,其底层使用到ConversionService和PropertyEditor。没错,他不支持入参校验。 |
支持哪些参数
@RequestParam注解,特殊情况:当Map数据类型时,要求@RequestParam的name属性存在才支持。否则由RequestParamMapMethodArgumentResolver提供支持,用于获取所有的地址栏参数。
怎么解析的
核心逻辑在他的父类方法AbstractNamedValueMethodArgumentResolver#resolveArgument里面
而通过参数名获取参数值,则由对应的实际子类来实现。不用说,对于@RequestParam而言,肯定是RequestParamMethodArgumentResolver从request中获取了。不过,不是简单的request.getParameterValues(paramName),因为参数类型还可能是MultipartFile类型,就需要调用另外的方法获取了。
此外,值得留意的是,他并不会进行参数校验。
下次我们就聊开篇谈到的三个问题中的第二个:怎么处理方法调用的返回值。
上一篇:
探索SpringMVC-九大组件之HandlerAdapter
下一篇:
探索SpringMVC-HandlerAdapter之RequestMappingHandlerAdapter-返回值处理
第一篇:
探索SpringMVC-web上下文