1.参数解析器简介
上一篇章稍微研究了方法处理器(HandlerMethod),本篇稍微研究参数解析器(HandlerMethodArgumentResolver)。参数解析器主要用于根据目标方法的参数描述(比如参数类型/参数名/注解),从各种http对象(比如request/session/header)中获取参数值;
1.1.类结构
1.2.功能说明(重点)
名称以ReturnValueHandler结尾的是单一的参数解析器,名称以MethodProcessor结尾的既是参数解析器,也是返回值处理器;
分类 | 名称 | 支持的类型 | 功能 |
---|---|---|---|
获取request对象 | ServletRequestMethodArgumentResolver | 直接获取Request,HttpSession, PushBuilder,HttpMethod等对象 | 参数解析器 |
获取response对象 | ServletResponseMethodArgumentResolver | 直接获取ServletResponse, OutputStream, Writer对象 | 参数解析器 |
获取requestHeader参数 | RequestHeaderMethodArgumentResolver | @RequestHeader标注的参数(非Map类型) | 参数解析器 |
RequestHeaderMapMethodArgumentResolver | 1.@RequestHeader标注的Map参数(同参数取第一个值) 2.@RequestHeader标注的MultiValueMap参数(同参数取所有值) | 参数解析器 | |
获取requestParamter参数 | RequestParamMethodArgumentResolver | 1.@RequestParam标注的普通参数(非Map类型) 2.@RequestParam标注的MultipartFile参数 3.没用@RequestParam标注的简单类型(int/long) 4.@RequestParam标注的Map类型参数(需在注解中指定参数名) | 参数解析器 |
RequestParamMapMethodArgumentResolver | 1.@RequestParam标注的Map类型参数(没有指定参数名。同参数取第一个值,文件则取所有文件) 2.@RequestParam标注MultiValueMap参数(没有指定参数名。同参数取所有值) | 参数解析器 | |
ModelAttributeMethodProcessor (从model中获取值) | 1.@ModelAttribute标注的参数 2.@ModelAttribute标注的方法 | 参数解析器&返回值处理器 | |
ServletModelAttributeMethodProcessor (依次从url/requestParameter/model中获取值) | 1.@ModelAttribute标注的参数 2.@ModelAttribute标注的方法 3.所有非简单类型/简单类型数组(annotationNotRequired=true) | 参数解析器&返回值处理器 | |
ExpressionValueMethodArgumentResolver | @Value标注的参数(支持${...}表#{...}表达式) | 参数解析器 | |
获取requestAttribute参数 | ServletWebArgumentResolverAdapter | WebArgumentResolver支持的类型 实现WebArgumentResolver自定义参数解析 | 参数解析器适配器 |
RequestAttributeMethodArgumentResolver | @RequestAttribute标注的参数 | 参数解析器 | |
获取serssionAttribute参数 | SessionAttributeMethodArgumentResolver | @SessionAttribute标注的参数 | 参数解析器 |
获取url参数 | PathVariableMethodArgumentResolver | 1.@PathVariable标注的普通参数(非Map类型) 2.@PathVariable标注的Map类型参数(需在注解中指定参数名) | 参数解析器 |
PathVariableMapMethodArgumentResolver | @PathVariable标注的Map类型参数(没有指定参数名) | 参数解析器 | |
MatrixVariableMethodArgumentResolver | 1.@MatrixVariable标注的普通参数(非Map类型) 2.@MatrixVariable标注的Map类型参数(需在注解中指定参数名) | 参数解析器 | |
MatrixVariableMapMethodArgumentResolver | @MatrixVariable标注的Map类型参数(没有指定参数名) | 参数解析器 | |
获取requestBody参数 | RequestPartMethodArgumentResolver | 1.@RequestPart注解标注的参数 2.MultipartFile类型参数 3.Servlet3.0的javax.servlet.http.Part类型参数 | 参数解析器 |
RequestResponseBodyMethodProcessor | 1.@RequestBody标注的参数 2.@ResponseBody标注的方法 | 参数解析器&返回值处理器 | |
HttpEntityMethodProcessor | 1.HttpEntity和RequestEntity类型的参数 2.返回HttpEntity和ResponseEntity类型的方法 | 参数解析器&返回值处理器 |
2.参数解析器部分源码
2.1.根据参数名获取值
AbstractNamedValueMethodArgumentResolver
扩展根据参数名获取参数值的功能。参数名一般为参数的注解配置的名称,如果没有注解或者注册没有配置名称,则为参数的名称。所有根据参数名称获取参数值的参数解析器都是它的子类。具体过程如下:
- 获取目标获取目标方法参数的基本信息(参数名称&是否必须&默认值);
- 解析配置的参数名(通常由注解配置,参数名支持表达式);
- 根据目标方法的参数名获取参数值;
- 如果参数值为空,则先解析配置的默认值,然后处理参数值缺失,最后处理空值;
- 解析完成之后处理参数值;
public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver {
//**获取目标方法参数注解配置的属性
@Nullable
private final ConfigurableBeanFactory configurableBeanFactory;
//**解析注解配置属性中的表达式
@Nullable
private final BeanExpressionContext expressionContext;
//**缓存解析过的目标方法参数
private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256);
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//**获取目标方法参数的基本信息(参数名称&是否必须&默认值)
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
//**是否是Optional类型参数
MethodParameter nestedParameter = parameter.nestedIfOptional();
//**解析参数名(通常由注解配置,参数名支持表达式)
Object resolvedName = resolveStringValue(namedValueInfo.name);
...
//**根据目标方法的参数名获取参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
//**如果参数值为空&s配置了默认值,则解析默认值
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
//**如果参数值为空&参数是必须的&参数不是Optional类型,则处理参数值缺失
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
//**如果参数值为空,则处理空值
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
...
//**解析完成之后处理参数值
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
//**获取目标方法参数信息(NamedValueInfo封装了目标方法参数名称&是否必须&默认值),
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
namedValueInfo = createNamedValueInfo(parameter);
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
//**根据目标方法参数信息创建NamedValueInfo对象,获取目标方法参数的名称/是否必须/默认值
protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);
//**更新目标方法的参数信息
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
String name = info.name;
if (info.name.isEmpty()) {
name = parameter.getParameterName();
...
}
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
//**解析参数配置的名称/默认值
@Nullable
private Object resolveStringValue(String value) {
if (this.configurableBeanFactory == null) {
return value;
}
String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
if (exprResolver == null || this.expressionContext == null) {
return value;
}
return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
}
//**根据目标方法的参数名获取参数值
@Nullable
protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request)
throws Exception;
//**处理参数值缺失(目标方法的参数不能为空 & 没有默认值 & resolveName返回null时调用)
protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request)
throws Exception {
handleMissingValue(name, parameter);
}
//**处理参数值缺失(目标方法的参数不能为空 & 没有默认值 & resolveName返回null时调用)
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException {
throw new ServletRequestBindingException("Missing argument '" + name +
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
}
//**解析之后参数值为空,进行额外处理
@Nullable
private Object handleNullValue(String name, @Nullable Object value, Class<?> paramType) {
if (value == null) {
if (Boolean.TYPE.equals(paramType)) {
return Boolean.FALSE;
}
...
}
return value;
}
//**解析之后处理参数值
protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter,
@Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
}
}
2.2.获取body参数
AbstractMessageConverterMethodArgumentResolver
扩展解析request中body内容,并转换为目标方法的参数类型的功能。所有从body解析参数的参数解析器都是它的子类。具体过程如下:
- 获取基本信息(http请求content-type、header和body,并获取目标方法参数的类对象targetClass);
- 根据content-type和targetClass找到支持的消息转换器;
- 如果body的内容为空,则直接调用解析之后调用RequestResponseBodyAdviceChain.handleEmptyBody处理空值;
- 解析之前调用RequestResponseBodyAdviceChain.beforeBodyRead进行处理;
- 调用消息转换器read()从body中解析目标方法的参数值;
- 解析之后调用RequestResponseBodyAdviceChain.afterBodyRead进行处理;
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
//**支持的http方法
private static final Set<HttpMethod> SUPPORTED_METHODS =
EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);
//**空值
private static final Object NO_VALUE = new Object();
//**消息转换器,读取body内容并转换为目标方法的参数
protected final List<HttpMessageConverter<?>> messageConverters;
//**支持的http Media类型
protected final List<MediaType> allSupportedMediaTypes;
//**读取body内容前/后进行额外的处理
private final RequestResponseBodyAdviceChain advice;
//**获取支持的Media类型(实际是HttpMessageConverter支持的Media类型)
private static List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<>();
for (HttpMessageConverter<?> messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
List<MediaType> result = new ArrayList<>(allSupportedMediaTypes);
MediaType.sortBySpecificity(result);
return Collections.unmodifiableList(result);
}
//**根据目标方法参数读取request中body的内容
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
//**获取http header中content-type,默认为application/octet-stream
MediaType contentType;
boolean noContentType = false;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
//**获取目标方法参数的class对象
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
//**获取http方法
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
try {
//**获取request中的headers和body;
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
//**遍历所有的消息转换器,找到支持http content-type和目标方法的参数类型的消息转换器,解析目标方法的参数值
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) { //**body内容不为空,则解析目标方法的参数值
//**解析之前进行处理
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
//**解析之后进行处理
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else { //**否则,处理空值
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
...
return body;
}
//**获取HttpInputMessage
protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
return new ServletServerHttpRequest(servletRequest);
}
//**参数验证
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
binder.validate(validationHints);
break;
}
}
}
//**参数验证错误是否抛出异常(如果参数后面是一个BindingResult参数,则不会抛出异常)
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
int i = parameter.getParameterIndex();
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
//**如果参数后面是一个BindingResult(Errors的子类)参数,则不会抛出异常
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
//**如果目标方法的参数是Optional类型,则把解析的参数值包装成Optional
@Nullable
protected Object adaptArgumentIfNecessary(@Nullable Object arg, MethodParameter parameter) {
if (parameter.getParameterType() == Optional.class) {
if (arg == null || (arg instanceof Collection && ((Collection<?>) arg).isEmpty()) ||
(arg instanceof Object[] && ((Object[]) arg).length == 0)) {
return Optional.empty();
}
else {
return Optional.of(arg);
}
}
return arg;
}
}
2.3.参数解析器适配器
A.AbstractWebArgumentResolverAdapter
把WebArgumentResolver转换为参数解析器的适配器。需要和自定义的WebArgumentResolver一起使用,解析自定义的参数类型;
public abstract class AbstractWebArgumentResolverAdapter implements HandlerMethodArgumentResolver {
//**WebArgumentResolver
private final WebArgumentResolver adaptee;
//**使用WebArgumentResolver判断是否能解析目标参数
@Override
public boolean supportsParameter(MethodParameter parameter) {
try {
NativeWebRequest webRequest = getWebRequest();
Object result = this.adaptee.resolveArgument(parameter, webRequest);
if (result == WebArgumentResolver.UNRESOLVED) {
return false;
}
else {
return ClassUtils.isAssignableValue(parameter.getParameterType(), result);
}
}
...
}
//**使用WebArgumentResolver解析目标参数
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
Object result = this.adaptee.resolveArgument(parameter, webRequest);
...
}
return result;
}
//**获取NativeWebRequest
protected abstract NativeWebRequest getWebRequest();
}
B.WebArgumentResolver
WebArgumentResolver是一个函数式接口,是spring预留用于解析用户自定义参数类型的SPI。使用该接口可以很方便的自定义参数解析器,解析自定义的参数类型。我觉得自定义参数解析器应该优先考虑该接口;
@FunctionalInterface
public interface WebArgumentResolver {
//**解析目标方法的参数
@Nullable
Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception;
}