Spring-重复请求参数分析
实现结果
- 当出现重复参数名时,所有参数值均会保存在 map 的 相同 key(即参数名)中。
- 若参数格式为 String,则所有参数值用逗号连接,一次性输出。
- 若参数格式为 Integer,则只输出第一个参数值。
实现分析
参数均用 @RequestParam
注解修饰,则应当是与之相关的参数绑定环节中实现了这样特殊的参数处理。
参数存储
使用 RequestParamMethodArgumentResolver 解析
@RequestParam
注释的方法参数,将相同参数名下的所有参数值均存储在一个 String 数组中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
...
Object arg = null;
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
...
}
}通过
WebDataBinder
的convertIfNecessary
方法,实现参数转换处理。
1 | public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver |
转换处理
- 通过
WebDataBinder
、DataBinder
、TypeConverterSupport
层层转包至TypeConverterDelegate
的convertIfNecessary
方法处理。 - 通过判断所需格式与当前格式,进行参数转换处理。
String[] To String
确认所需类型为 String,有合适的
PropertyEditor
,但转换值相对于所需类型不可赋值,且当前值的类型不为 String。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public <T> T convertIfNecessary( String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor)throws IllegalArgumentException {
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
Object convertedValue = newValue;
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
...
}确认当前转换值的类型为 String[],但所需类型不为数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14private Object doConvertValue( Object oldValue, Object newValue,
{ Class<?> requiredType, PropertyEditor editor)
...
if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
// Convert String array to a comma-separated String.
// Only applies if no PropertyEditor converted the String array before.
// The CSV String will be passed into a PropertyEditor's setAsText method, if any.
if (logger.isTraceEnabled()) {
logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
}
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
}
...
}默认使用逗号作为分隔符,将所有数组的所有元素拼接为一个字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public abstract class StringUtils {
public static String arrayToCommaDelimitedString( { Object[] arr)
return arrayToDelimitedString(arr, ",");
}
public static String arrayToDelimitedString( { Object[] arr, String delim)
if (ObjectUtils.isEmpty(arr)) {
return "";
}
if (arr.length == 1) {
return ObjectUtils.nullSafeToString(arr[0]);
}
StringJoiner sj = new StringJoiner(delim);
for (Object elem : arr) {
sj.add(String.valueOf(elem));
}
return sj.toString();
}
}
String[] To Integer
确认当前转换值的类型为 Integer,没有合适的
PropertyEditor
,使用ConversionService
实现。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public <T> T convertIfNecessary( String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor)throws IllegalArgumentException {
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
根据源数据类型与目标数据类型,选择合适的
GenericConverter
,通过ConversionUtils
的invokeConverter
方法实现。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public class GenericConversionService implements ConfigurableConversionService {
public Object convert( { Object source, TypeDescriptor sourceType, TypeDescriptor targetType)
...
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
}
abstract class ConversionUtils {
public static Object invokeConverter(GenericConverter converter, Object source,
TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Throwable ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
}选择数组的第一个元素,作为参数值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16final class ArrayToObjectConverter implements ConditionalGenericConverter {
public Object convert( { Object source, TypeDescriptor sourceType, TypeDescriptor targetType)
if (source == null) {
return null;
}
if (sourceType.isAssignableTo(targetType)) {
return source;
}
if (Array.getLength(source) == 0) {
return null;
}
Object firstElement = Array.get(source, 0);
return this.conversionService.convert(firstElement, sourceType.elementTypeDescriptor(firstElement), targetType);
}
}