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
public interface Converter<S, T> {


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
public interface GenericConverter {


Set<ConvertiblePair> getConvertibleTypes();


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
public interface ConversionService {

boolean canConvert(Class<?> sourceType, Class<?> targetType);

boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

<T> T convert(Object source, Class<T> targetType);

Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}

数据验证

Validator接口用于数据验证

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

// 判断当前验证器是否支持该Class类型的验证
boolean supports(Class<?> clazz);


// 如果supports返回true,则执行该方法验证逻辑
void validate(Object target, Errors errors);

}

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

通过WebDataBinder#setValidator来添加验证器

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