参数解析
说到参数解析,springmvc中处理参数的是HandlerMethodArgumentResolver接口
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 类型的参数
这些解析器是在执行
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
执行对应的Controller方法前来进行参数解析时调用的org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
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来进行参数解析
遍历消息解析器找到可以进行解析该类型的消息解析器
for (HttpMessageConverter<?> converter : this.messageConverters) {
对于简单的参数会以简单的转换器进行转换,而这些简单的转换器是Spring MVC自身已经提供了的,但是如果是转换HTTP请求体,会调用HttpMessageConverter接口的方法对请求体的信息进行转换
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接口是一个普通的转换器
public interface Converter<S, T> {
T convert(S source);
}
可以将某个类型转换为另一个类型
Formatter接口
Formatter接口是一个格式化转换器,如将日期字符串格式化
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
GenericConverter接口
GenericConverter接口是将HTTP参数转换为数组
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接口
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接口用于数据验证
public interface Validator {
// 判断当前验证器是否支持该Class类型的验证
boolean supports(Class<?> clazz);
// 如果supports返回true,则执行该方法验证逻辑
void validate(Object target, Errors errors);
}
WebDataBinder还可以进行验证,使用@InitBinder注解可以允许在进入控制器方法之前修改WebDataBinder机制,可以来设置验证器
通过WebDataBinder#setValidator来添加验证器
本文由mdnice多平台发布