Spring MVC : 框架缺省的控制器方法参数解析器

使用Spring MVC进行Web应用开发时,请求参数接收是很重要的一个方面。一般情况下,我们可以通过request.getParameter(name)这种方式来获取指定名称参数的值,这种方式是由Servlet容器自身提供的。但使用这种方式会导致开发人员必须在控制器方法中书写仅用于接收参数的逻辑,在参数量多的情况下,这部分代码甚至占据了方法体的大部分,而核心业务逻辑反而淹没其中。为了使开发人员从这种窘境中解脱出来提高生产效率,关注核心业务,Spring MVC提供了各种便利工具来接收参数,同时也将该关注点从主业务逻辑中分离出来。

Spring MVC所提供的这些工具,有@PathVariable,@RequestParam,@RequestBody,@RequestParam,@CookieValue等注解,也有对一些类直接支持逻辑。本文不讲这些注解或者被支持的类如何使用以及它们的目的,而是看看针对这些便利工具,Spring MVC在背后所提供的参数解析器:HandlerMethodArgumentResolver

下表是Spring 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应用缺省使用类型为RequestMappingHandlerAdapterbean组件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;
	}

上面方法中参数解析器添加的顺序很重要,因为为每个参数寻找参数解析器时,会按照以上添加顺序去寻找第一个能够解析该参数的参数解析器。

上一篇:SpringCloud常用组件


下一篇:Java之SpringBoot自定义配置与整合Druid