Spring MVC中使用HandlerMethodArgumentResolver
策略接口来定义处理器方法参数解析器,@RequestParam
使用的是RequestParamMapMethodArgumentResolver
和RequestParamMethodArgumentResolver
,接下来一块儿来深刻了解一下其源码实现。java
类结构web
类解析spring
HandlerMethodArgumentResolver
和AbstractNamedValueMethodArgumentResolver
是解析策略的上层定义和抽象,关于这两个类能够参照《Spring 注解面面通 之 @CookieValue参数绑定源码解析》中的解析。数组
RequestParamMapMethodArgumentResolver
和RequestParamMethodArgumentResolver
则是用来针对不用类型的方法参数的解析。ide
1) RequestParamMapMethodArgumentResolver
实现了HandlerMethodArgumentResolver
的supportsParameter(...)
和resolveArgument(...)
方法。网站
RequestParamMapMethodArgumentResolver
相对比较简单,但在某些条件成立的状况下才会使用此类进行解析:ui
① 方法参数由@RequestParam
注解注释。this
② 方法参数类型必须是Map
类型。url
③ @RequestParam
注解的name
不能有值。spa
resolveArgument(...)
在解析参数时,从NativeWebRequest
(HttpServletRequest
的包装)中获取全部参数,针对MultiValueMap
和普通Map
两种参数类型进行处理:
① 参数类型为MultiValueMap
时,返回LinkedMultiValueMap
实例,包含全部请求参数。
② 参数类型为Map
时,返回LinkedHashMap
实例,包含全部请求参数。
package org.springframework.web.method.annotation; import java.util.LinkedHashMap; import java.util.Map; import org.springframework.core.MethodParameter; import org.springframework.lang.Nullable; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; /** * 解析用@RequestParam注释的Map类型方法参数,其中未指定请求参数名称. * * 建立的Map包含全部请求参数名称/值对. * 若是方法参数类型是MultiValueMap,那么对于请求参数具备多个值的状况, * 建立的映射包含全部请求参数和值. */ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver { /** * 方法参数检查. * 方法参数由@RequestParam注释,且@RequestParam的name属性为空. * 方法参数类型必须为Map类型. */ @Override public boolean supportsParameter(MethodParameter parameter) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(requestParam.name())); } /** * 解析方法参数值. */ @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Class<?> paramType = parameter.getParameterType(); // 获取请求的全部参数. Map<String, String[]> parameterMap = webRequest.getParameterMap(); // 方法参数类型为MultiValueMap. if (MultiValueMap.class.isAssignableFrom(paramType)) { MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size()); parameterMap.forEach((key, values) -> { for (String value : values) { result.add(key, value); } }); return result; } // 方法参数类型为非MultiValueMap的Map类型. else { Map<String, String> result = new LinkedHashMap<>(parameterMap.size()); parameterMap.forEach((key, values) -> { if (values.length > 0) { result.put(key, values[0]); } }); return result; } } }
2) RequestParamMethodArgumentResolver
继承自抽象AbstractNamedValueMethodArgumentResolver
(能够参照《Spring 注解面面通 之 @CookieValue参数绑定源码解析》)。
RequestParamMethodArgumentResolver
除了能处理@RequestParam
注解外,还能够处理@RequestPart
注解:
当处理@RequestParam
注解时,需在某些条件成立的状况下才会使用此类进行解析:
① 方法参数由@RequestParam
注解注释。
② 方法参数如果Map
类型时,@RequestParam
的name
属性不能为空。
③ 方法参数若不是Map类型时,均可以处理。
当处理@RequestPart
注解时,需在某些条件成立的状况下才会使用此类进行解析:
① 方法参数不可由@RequestPart
注解注释。
② 方法参数类型为org.springframework.web.multipart.MultipartFile
、org.springframework.web.multipart.MultipartFile
集合、org.springframework.web.multipart.MultipartFile
数组、javax.servlet.http.Part
、javax.servlet.http.Part
集合或javax.servlet.http.Part
数组。
③ 一个简单类型的方法参数,包括:boolean
、byte
、char
、short
、int
、long
、float
、double
、Enum.class
、CharSequence.class
、Number
.class、Date
.class、URI
.class、URL.class
、Locale.class
或Class.class
。
package org.springframework.web.method.annotation; import java.beans.PropertyEditor; import java.util.Collection; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ValueConstants; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.UriComponentsContributor; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.multipart.support.MultipartResolutionDelegate; import org.springframework.web.util.UriComponentsBuilder; /** * 解析用@RequestParam注释的方法参数,MultipartFile类型的参数与 * Spring的{@link MultipartResolver}抽象结合使用,以及AAA类型的参数与Servlet 3.0多部分请求一块儿使用. * 这个解析器也能够在默认的解析模式下建立,在这种模式下,没有用RequestParam注释的简单类型(int、long等)也被视为请求参数,参数名从参数名派生. * * 若是方法参数类型是Map,则使用注释中指定的名称来解析请求参数字符串值. * 而后经过类型转换将该值转换为Map,假设已经注册了合适的Converter或PropertyEditor. * 或者,若是没有指定请求参数名,则使用RequestParamMapMethodArgumentResolver以映射的形式提供对全部请求参数的访问. * * 调用@WebDataBinder将类型转换应用于还没有与方法参数类型匹配的已解析请求头值. */ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver implements UriComponentsContributor { /** * String 类型描述符. */ private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); private final boolean useDefaultResolution; /** * @param useDefaultResolution 在默认解析模式下,一个简单类型的方法参数, * 如BeanUtils.isSimpleProperty中定义的那样,被视为一个请求参数, * 即便它没有被注释,请求参数名是从方法参数名派生的. */ public RequestParamMethodArgumentResolver(boolean useDefaultResolution) { this.useDefaultResolution = useDefaultResolution; } /** * @param beanFactory 一个Bean工厂,用于解析默认值中的${…}占位符和#{…}SpEL表达式,若是默认值不包含表达式,则为null. * @param useDefaultResolution 在默认解析模式下,一个简单类型的方法参数, * 如BeanUtils.isSimpleProperty中定义的那样,被视为一个请求参数, * 即便它没有被注释,请求参数名是从方法参数名派生的. */ public RequestParamMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory, boolean useDefaultResolution) { super(beanFactory); this.useDefaultResolution = useDefaultResolution; } /** * 方法参数检查: * 方法参数由@RequestParam注释,且@RequestParam的name属性为空. * 方法参数类型为Map类型. * 方法参数不可由@RequestPart注释. */ @Override public boolean supportsParameter(MethodParameter parameter) { // 处理@RequestParam注解. if (parameter.hasParameterAnnotation(RequestParam.class)) { if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && StringUtils.hasText(requestParam.name())); } else { return true; } } // 处理@RequestPart注解. else { if (parameter.hasParameterAnnotation(RequestPart.class)) { return false; } parameter = parameter.nestedIfOptional(); if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { return true; } else if (this.useDefaultResolution) { return BeanUtils.isSimpleProperty(parameter.getNestedParameterType()); } else { return false; } } } /** * 建立NamedValueInfo. */ @Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { RequestParam ann = parameter.getParameterAnnotation(RequestParam.class); return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); } /** * 解析方法参数值. */ @Override @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); // 解析多部分请求值. if (servletRequest != null) { Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest); if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) { return mpArg; } } Object arg = null; MultipartHttpServletRequest multipartRequest = request.getNativeRequest(MultipartHttpServletRequest.class); // 解析多部分请求值. if (multipartRequest != null) { List<MultipartFile> files = multipartRequest.getFiles(name); if (!files.isEmpty()) { arg = (files.size() == 1 ? files.get(0) : files); } } // 解析普通参数值. if (arg == null) { String[] paramValues = request.getParameterValues(name); if (paramValues != null) { arg = (paramValues.length == 1 ? paramValues[0] : paramValues); } } return arg; } /** * 处理参数缺失异常. */ @Override protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { if (servletRequest == null || !MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { throw new MultipartException("Current request is not a multipart request"); } else { throw new MissingServletRequestPartException(name); } } else { throw new MissingServletRequestParameterException(name, parameter.getNestedParameterType().getSimpleName()); } } @Override public void contributeMethodArgument(MethodParameter parameter, @Nullable Object value, UriComponentsBuilder builder, Map<String, Object> uriVariables, ConversionService conversionService) { Class<?> paramType = parameter.getNestedParameterType(); if (Map.class.isAssignableFrom(paramType) || MultipartFile.class == paramType || Part.class == paramType) { return; } RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); String name = (requestParam == null || StringUtils.isEmpty(requestParam.name()) ? parameter.getParameterName() : requestParam.name()); Assert.state(name != null, "Unresolvable parameter name"); if (value == null) { if (requestParam != null && (!requestParam.required() || !requestParam.defaultValue().equals(ValueConstants.DEFAULT_NONE))) { return; } builder.queryParam(name); } else if (value instanceof Collection) { for (Object element : (Collection<?>) value) { element = formatUriValue(conversionService, TypeDescriptor.nested(parameter, 1), element); builder.queryParam(name, element); } } else { builder.queryParam(name, formatUriValue(conversionService, new TypeDescriptor(parameter), value)); } } @Nullable protected String formatUriValue( @Nullable ConversionService cs, @Nullable TypeDescriptor sourceType, @Nullable Object value) { if (value == null) { return null; } else if (value instanceof String) { return (String) value; } else if (cs != null) { return (String) cs.convert(value, sourceType, STRING_TYPE_DESCRIPTOR); } else { return value.toString(); } } /** * RequestParamNamedValueInfo. */ private static class RequestParamNamedValueInfo extends NamedValueInfo { public RequestParamNamedValueInfo() { super("", false, ValueConstants.DEFAULT_NONE); } public RequestParamNamedValueInfo(RequestParam annotation) { super(annotation.name(), annotation.required(), annotation.defaultValue()); } } }
总结
@RequestParam
是用来处理Web请求头中的信息,随着网站的多样和多元化,@RequestParam
使用频率会愈来愈普遍。
若文中存在错误和不足,欢迎指正!