Spring MVC中使用
HandlerMethodArgumentResolver
策略接口来定义处理器方法参数解析器,
@RequestParam
使用的是
RequestParamMapMethodArgumentResolver
和
RequestParamMethodArgumentResolver
,接下来一起来深入了解一下其源码实现。
类结构
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNyZuBnL0MDNwIDMxgTM1AjMxAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
类解析
HandlerMethodArgumentResolver
和
AbstractNamedValueMethodArgumentResolver
是解析策略的上层定义和抽象,关于这两个类可以参照《Spring 注解面面通 之 @CookieValue参数绑定源码解析》中的解析。
RequestParamMapMethodArgumentResolver
和
RequestParamMethodArgumentResolver
则是用来针对不用类型的方法参数的解析。
1)
RequestParamMapMethodArgumentResolver
实现了
HandlerMethodArgumentResolver
的
supportsParameter(...)
和
resolveArgument(...)
方法。
RequestParamMapMethodArgumentResolver
相对比较简单,但在某些条件成立的情况下才会使用此类进行解析:
① 方法参数由
@RequestParam
注解注释。
② 方法参数类型必须是
Map
类型。
③
@RequestParam
注解的
name
不能有值。
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
使用频率会越来越广泛。
若文中存在错误和不足,欢迎指正!