0%

参数解析

参数解析

说到参数解析,springmvc中处理参数的是HandlerMethodArgumentResolver接口

1
2
3
4
5
6
7
8
9
10
public interface HandlerMethodArgumentResolver {

// 判断是否支持该类型参数
boolean supportsParameter(MethodParameter parameter);

// 进行参数解析
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;

}

其有一个抽象实现类AbstractNamedValueMethodArgumentResolver,有很多有用的子类

  • MapMethodProcessor

    这个用来处理 Map/ModelMap 类型的参数,解析完成后返回 model。

  • PathVariableMethodArgumentResolver

    这个用来处理使用了 @PathVariable 注解并且参数类型不为 Map 的参数,参数类型为 Map 则使用 PathVariableMapMethodArgumentResolver 来处理。

  • PathVariableMapMethodArgumentResolver

    见上。

  • ErrorsMethodArgumentResolver

    这个用来处理 Error 参数,例如我们做参数校验时的 BindingResult。

  • AbstractNamedValueMethodArgumentResolver

    这个用来处理 key/value 类型的参数,如请求头参数、使用了 @PathVariable 注解的参数以及 Cookie 等。

  • RequestHeaderMethodArgumentResolver

    这个用来处理使用了 @RequestHeader 注解,并且参数类型不是 Map 的参数(参数类型是 Map 的使用 RequestHeaderMapMethodArgumentResolver)。

  • RequestHeaderMapMethodArgumentResolver

    见上。

  • RequestAttributeMethodArgumentResolver

    这个用来处理使用了 @RequestAttribute 注解的参数。

  • RequestParamMethodArgumentResolver

    这个功能就比较广了。使用了 @RequestParam 注解的参数、文件上传的类型 MultipartFile、或者一些没有使用任何注解的基本类型(Long、Integer)以及 String 等,都使用该参数解析器处理。需要注意的是,如果 @RequestParam 注解的参数类型是 Map,则该注解必须有 name 值,否则解析将由 RequestParamMapMethodArgumentResolver 完成。

  • RequestParamMapMethodArgumentResolver

    见上。

  • AbstractCookieValueMethodArgumentResolver

    这个是一个父类,处理使用了 @CookieValue 注解的参数。

  • ServletCookieValueMethodArgumentResolver

    这个处理使用了 @CookieValue 注解的参数。

  • MatrixVariableMethodArgumentResolver

    这个处理使用了 @MatrixVariable 注解并且参数类型不是 Map 的参数,如果参数类型是 Map,则使用 MatrixVariableMapMethodArgumentResolver 来处理。

  • MatrixVariableMapMethodArgumentResolver

    见上。

  • SessionAttributeMethodArgumentResolver

    这个用来处理使用了 @SessionAttribute 注解的参数。

  • ExpressionValueMethodArgumentResolver

    这个用来处理使用了 @Value 注解的参数。

  • ServletResponseMethodArgumentResolver

    这个用来处理 ServletResponse、OutputStream 以及 Writer 类型的参数。

  • ModelMethodProcessor

    这个用来处理 Model 类型参数,并返回 model。

  • ModelAttributeMethodProcessor

    这个用来处理使用了 @ModelAttribute 注解的参数。

  • SessionStatusMethodArgumentResolver

    这个用来处理 SessionStatus 类型的参数。

  • PrincipalMethodArgumentResolver

    这个用来处理 Principal 类型参数

  • AbstractMessageConverterMethodArgumentResolver

    这是一个父类,当使用 HttpMessageConverter 解析 requestbody 类型参数时,相关的处理类都会继承自它。

  • RequestPartMethodArgumentResolver

    这个用来处理使用了 @RequestPart 注解、MultipartFile 以及 Part 类型的参数。

  • RequestResponseBodyMethodProcessor

    这个用来处理添加了 @RequestBody 注解的参数。

  • HttpEntityMethodProcessor

    这个用来处理 HttpEntity 和 RequestEntity 类型的参数。

  • ServletWebArgumentResolverAdapter

    这个给父类提供 request。

  • UriComponentsBuilderMethodArgumentResolver

    这个用来处理 UriComponentsBuilder 类型的参数。

  • ServletRequestMethodArgumentResolver

    这个用来处理 WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 类型的参数。

  • HandlerMethodArgumentResolverComposite

    这个看名字就知道是一个组合解析器,它是一个代理,具体代理其他干活的那些参数解析器。

  • RedirectAttributesMethodArgumentResolver

    这个用来处理 RedirectAttributes 类型的参数

这些解析器是在执行

1
2
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

执行对应的Controller方法前来进行参数解析时调用的org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues

1
2
3
4
5
6
7
8
9
10
11
12
13
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}

RequestResponseBodyMethodProcessor调用消息解析器

我之前只知道解析@RequestBody需要使用消息解析器HttpMessageConverter,但是没有深究是从哪调用的。突然看到消息解析我才知道原来是RequestResponseBodyMethodProcessor调用的。org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument来进行参数解析

遍历消息解析器找到可以进行解析该类型的消息解析器

1
for (HttpMessageConverter<?> converter : this.messageConverters) {

对于简单的参数会以简单的转换器进行转换,而这些简单的转换器是Spring MVC自身已经提供了的,但是如果是转换HTTP请求体,会调用HttpMessageConverter接口的方法对请求体的信息进行转换

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
public interface HttpMessageConverter<T> {


// 是否可读,clazz为java类型,mediaType为HTTP请求类型
boolean canRead(Class<?> clazz, MediaType mediaType);


// 判断clazz类型是否能够转换为mediaType媒体类型,其中clazz为java类型,mediaType为HTTP响应类型
boolean canWrite(Class<?> clazz, MediaType mediaType);


// 可支持的媒体类型列表
List<MediaType> getSupportedMediaTypes();


// 当canRead验证通过后,读入HTTP请求信息
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;


// 单canWrite方法验证通过后,写入响应
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;

}

HttpMessageConverter接口是将HTTP请求体转换为对应的java对象,对于HTTP参数和其他内容需要使用参数转换规则

参数转换

Spring MVC中,是通过WebDataBinder机制来获取参数的,它的主要作用是解析HTTP请求的上下文,然后在控制器的调用之前转换参数并且提供验证的功能,为调用控制器方法做准备。处理器会从HTTP请求中读取数据,然后通过三种接口进行各类参数转换(Converter、Formatter、GenericConverter)。

Converter接口

Converter接口是一个普通的转换器

1
2
3
4
5
6
7
8
9
10
11
public interface Converter<S, T> {

/**
* Convert the source object of type {@code S} to target type {@code T}.
* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
* @return the converted object, which must be an instance of {@code T} (potentially {@code null})
* @throws IllegalArgumentException if the source cannot be converted to the desired target type
*/
T convert(S source);

}

可以将某个类型转换为另一个类型

Formatter接口

Formatter接口是一个格式化转换器,如将日期字符串格式化

1
2
3
public interface Formatter<T> extends Printer<T>, Parser<T> {

}

GenericConverter接口

GenericConverter接口是将HTTP参数转换为数组

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public interface GenericConverter {

/**
* Return the source and target types that this converter can convert between.
* <p>Each entry is a convertible source-to-target type pair.
* <p>For {@link ConditionalConverter conditional converters} this method may return
* {@code null} to indicate all source-to-target pairs should be considered.
*/
Set<ConvertiblePair> getConvertibleTypes();

/**
* Convert the source object to the targetType described by the {@code TypeDescriptor}.
* @param source the source object to convert (may be {@code null})
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return the converted object
*/
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);


/**
* Holder for a source-to-target class pair.
*/
final class ConvertiblePair {

private final Class<?> sourceType;

private final Class<?> targetType;

/**
* Create a new source-to-target pair.
* @param sourceType the source type
* @param targetType the target type
*/
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}

public Class<?> getSourceType() {
return this.sourceType;
}

public Class<?> getTargetType() {
return this.targetType;
}

@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || other.getClass() != ConvertiblePair.class) {
return false;
}
ConvertiblePair otherPair = (ConvertiblePair) other;
return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
}

@Override
public int hashCode() {
return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
}

@Override
public String toString() {
return (this.sourceType.getName() + " -> " + this.targetType.getName());
}
}

}

ConversionService接口

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public interface ConversionService {

/**
* Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}.
* <p>If this method returns {@code true}, it means {@link #convert(Object, Class)} is capable
* of converting an instance of {@code sourceType} to {@code targetType}.
* <p>Special note on collections, arrays, and maps types:
* For conversion between collection, array, and map types, this method will return {@code true}
* even though a convert invocation may still generate a {@link ConversionException} if the
* underlying elements are not convertible. Callers are expected to handle this exceptional case
* when working with collections and maps.
* @param sourceType the source type to convert from (may be {@code null} if source is {@code null})
* @param targetType the target type to convert to (required)
* @return {@code true} if a conversion can be performed, {@code false} if not
* @throws IllegalArgumentException if {@code targetType} is {@code null}
*/
boolean canConvert(Class<?> sourceType, Class<?> targetType);

/**
* Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}.
* The TypeDescriptors provide additional context about the source and target locations
* where conversion would occur, often object fields or property locations.
* <p>If this method returns {@code true}, it means {@link #convert(Object, TypeDescriptor, TypeDescriptor)}
* is capable of converting an instance of {@code sourceType} to {@code targetType}.
* <p>Special note on collections, arrays, and maps types:
* For conversion between collection, array, and map types, this method will return {@code true}
* even though a convert invocation may still generate a {@link ConversionException} if the
* underlying elements are not convertible. Callers are expected to handle this exceptional case
* when working with collections and maps.
* @param sourceType context about the source type to convert from
* (may be {@code null} if source is {@code null})
* @param targetType context about the target type to convert to (required)
* @return {@code true} if a conversion can be performed between the source and target types,
* {@code false} if not
* @throws IllegalArgumentException if {@code targetType} is {@code null}
*/
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

/**
* Convert the given {@code source} to the specified {@code targetType}.
* @param source the source object to convert (may be {@code null})
* @param targetType the target type to convert to (required)
* @return the converted object, an instance of targetType
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null}
*/
<T> T convert(Object source, Class<T> targetType);

/**
* Convert the given {@code source} to the specified {@code targetType}.
* The TypeDescriptors provide additional context about the source and target locations
* where conversion will occur, often object fields or property locations.
* @param source the source object to convert (may be {@code null})
* @param sourceType context about the source type to convert from
* (may be {@code null} if source is {@code null})
* @param targetType context about the target type to convert to (required)
* @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType}
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is {@code null},
* or {@code sourceType} is {@code null} but source is not {@code null}
*/
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}

数据验证

Validator接口用于数据验证

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
30
31
32
public interface Validator {

/**
* Can this {@link Validator} {@link #validate(Object, Errors) validate}
* instances of the supplied {@code clazz}?
* <p>This method is <i>typically</i> implemented like so:
* <pre class="code">return Foo.class.isAssignableFrom(clazz);</pre>
* (Where {@code Foo} is the class (or superclass) of the actual
* object instance that is to be {@link #validate(Object, Errors) validated}.)
* @param clazz the {@link Class} that this {@link Validator} is
* being asked if it can {@link #validate(Object, Errors) validate}
* @return {@code true} if this {@link Validator} can indeed
* {@link #validate(Object, Errors) validate} instances of the
* supplied {@code clazz}
*/
// 判断当前验证器是否支持该Class类型的验证
boolean supports(Class<?> clazz);

/**
* Validate the supplied {@code target} object, which must be
* of a {@link Class} for which the {@link #supports(Class)} method
* typically has (or would) return {@code true}.
* <p>The supplied {@link Errors errors} instance can be used to report
* any resulting validation errors.
* @param target the object that is to be validated (can be {@code null})
* @param errors contextual state about the validation process (never {@code null})
* @see ValidationUtils
*/
// 如果supports返回true,则执行该方法验证逻辑
void validate(Object target, Errors errors);

}

WebDataBinder还可以进行验证,使用@InitBinder注解可以允许在进入控制器方法之前修改WebDataBinder机制,可以来设置验证器

通过WebDataBinder#setValidator来添加验证器

欢迎关注我的其它发布渠道