使用Spring MVC进行Web应用开发时,请求参数接收是很重要的一个方面。一般情况下,我们可以通过request.getParameter(name)这种方式来获取指定名称参数的值,这种方式是由Servlet容器自身提供的。但使用这种方式会导致开发人员必须在控制器方法中书写仅用于接收参数的逻辑,在参数量多的情况下,这部分代码甚至占据了方法体的大部分,而核心业务逻辑反而淹没其中。为了使开发人员从这种窘境中解脱出来提高生产效率,关注核心业务,Spring MVC提供了各种便利工具来接收参数,同时也将该关注点从主业务逻辑中分离出来。
Spring MVC所提供的这些工具,有@PathVariable,@RequestParam,@RequestBody,@RequestParam,@CookieValue等注解,也有对一些类直接支持逻辑。本文不讲这些注解或者被支持的类如何使用以及它们的目的,而是看看针对这些便利工具,Spring MVC在背后所提供的参数解析器:HandlerMethodArgumentResolver。
下表是Spring Web MVC所提供的参数解析器实现类:
| 控制器方法参数解析器 | 所能解析的参数的类型特征 |
|---|---|
RequestParamMethodArgumentResolver | @RequestParam并且参数类型不是Map |
RequestParamMapMethodArgumentResolver | @RequestParam并且参数类型是Map |
PathVariableMethodArgumentResolver | @PathVariable并且参数类型不是Map |
PathVariableMapMethodArgumentResolver | @PathVariable并且参数类型是Map |
MatrixVariableMethodArgumentResolver | @MatrixVariable并且参数类型不是Map |
MatrixVariableMapMethodArgumentResolver | @MatrixVariable并且参数类型是Map |
ServletModelAttributeMethodProcessor | @ModelAttribute,Servlet相关 |
RequestResponseBodyMethodProcessor | @RequestBody,@ResponseBody |
RequestPartMethodArgumentResolver | @RequestPart |
RequestHeaderMethodArgumentResolver | @RequestHeader并且参数类型不是Map |
RequestHeaderMapMethodArgumentResolver | @RequestHeader并且参数类型是Map |
ServletCookieValueMethodArgumentResolver | @CookieValue |
ExpressionValueMethodArgumentResolver | @Value |
SessionAttributeMethodArgumentResolver | @SessionAttribute |
RequestAttributeMethodArgumentResolver | @RequestAttribute |
ServletRequestMethodArgumentResolver | WebRequest,ServletRequest,MultipartRequest,HttpSession,PushBuilder,Principal,InputStream,Reader,HttpMethod,Locale,TimeZone,java.time.ZoneId |
ServletResponseMethodArgumentResolver | ServletResponse,OutputStream,Writer |
HttpEntityMethodProcessor | HttpEntity,RequestEntity,ResponseEntity |
RedirectAttributesMethodArgumentResolver | RedirectAttributes |
ModelMethodProcessor | Model类型的参数和返回值 |
MapMethodProcessor | Map类型的参数和返回值 |
ErrorsMethodArgumentResolver | Errors |
SessionStatusMethodArgumentResolver | SessionStatus |
UriComponentsBuilderMethodArgumentResolver | UriComponentsBuilder |
Spring MVC应用缺省使用类型为RequestMappingHandlerAdapter 的bean组件requestMappingHandlerAdapter调用每个请求对应的控制器方法。在bean组件requestMappingHandlerAdapter初始化时,它会构建一个HandlerMethodArgumentResolverComposite对象,该对象是多个HandlerMethodArgumentResolver的一个集合:
// RequestMappingHandlerAdapter 代码片段
// RequestMappingHandlerAdapter 的初始化方法
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
// 获取缺省的 HandlerMethodArgumentResolver, 多个
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
// 构造 HandlerMethodArgumentResolverComposite 对象,
// 包装缺省的多个 HandlerMethodArgumentResolver
this.argumentResolvers =
new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers =
new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers =
new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
从上面的代码可见,缺省使用的控制器方法参数解析器由getDefaultInitBinderArgumentResolvers()提供。该方法实现如下:
// RequestMappingHandlerAdapter 代码片段
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(),
this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments , 添加定制的参数解析器
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all , 如果上面的参数解析器都处理不了,尝试使用下面的解析器尝试兜底
// true 表示 : 即使不使用注解 @RequestParam ,简单类型的方法参数也会被认为是请求参数被解析,
// 此时请求参数名称会根据方法参数名称派生而来。
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
// true 表示 : 即使不使用注解 @ModelAttribute,非简单类型的方法参数和返回值也会被认为
// 是 model attribute
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
上面方法中参数解析器添加的顺序很重要,因为为每个参数寻找参数解析器时,会按照以上添加顺序去寻找第一个能够解析该参数的参数解析器。
另外,以上这些的HandlerMethodArgumentResolver继承/实现关系如下所示 :
HandlerMethodArgumentResolverAbstractNamedValueMethodArgumentResolverAbstractCookieValueMethodArgumentResolverServletCookieValueMethodArgumentResolver
ExpressionValueMethodArgumentResolverRequestHeaderMethodArgumentResolverRequestParamMethodArgumentResolverMatrixVariableMethodArgumentResolverPathVariableMethodArgumentResolverRequestAttributeMethodArgumentResolverSessionAttributeMethodArgumentResolver
AbstractWebArgumentResolverAdapterServletWebArgumentResolverAdapter
ErrorsMethodArgumentResolverMapMethodProcessorModelAttributeMethodProcessorServletModelAttributeMethodProcessor
ModelMethodProcessorRequestHeaderMapMethodArgumentResolverRequestParamMapMethodArgumentResolverSessionStatusMethodArgumentResolverAbstractMessageConverterMethodArgumentResolverAbstractMessageConverterMethodProcessorHttpEntityMethodProcessorRequestResponseBodyMethodProcessor
RequestPartMethodArgumentResolver
MatrixVariableMapMethodArgumentResolverPathVariableMapMethodArgumentResolverRedirectAttributesMethodArgumentResolverServletRequestMethodArgumentResolverServletResponseMethodArgumentResolverUriComponentsBuilderMethodArgumentResolverHandlerMethodArgumentResolverComposite
本文介绍SpringMVC中参数解析器HandlerMethodArgumentResolver的工作原理及其重要实现类。通过不同的解析器,SpringMVC能有效处理@RequestParam、@PathVariable等注解,并支持简单及复杂类型的参数解析。
491

被折叠的 条评论
为什么被折叠?



