问题
问题解析
要了解这个错误出现的根本原因,你就需要了解请求参数的发生位置。 实际上,这里我们也能按注解名(@RequestParam)来确定解析发生的位置是在 RequestParamMethodArgumentResolver 中。为什么是它? 追根溯源,针对当前案例,当根据 URL 匹配上要执行的方法是 hi4 后,要反射调用它,必须解析出方法参数 name 和 address 才可以。而它们被 @RequestParam 注解修饰,所以解析器借助 RequestParamMethodArgumentResolver 就成了很自然的事情。 接下来我们看下 RequestParamMethodArgumentResolver 对参数解析的一些关键操作,参考其父类方法 AbstractNamedValueMethodArgumentResolver#resolveArgument:public final Object resolveArgument ( MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
NamedValueInfo namedValueInfo = getNamedValueInfo ( parameter) ;
MethodParameter nestedParameter = parameter. nestedIfOptional ( ) ;
Object arg = resolveName ( resolvedName. toString ( ) , nestedParameter, webRequest) ;
if ( arg == null ) {
if ( namedValueInfo. defaultValue != null ) {
arg = resolveStringValue ( namedValueInfo. defaultValue) ;
}
else if ( namedValueInfo. required && ! nestedParameter. isOptional ( ) ) {
handleMissingValue ( namedValueInfo. name, nestedParameter, webRequest) ;
}
arg = handleNullValue ( namedValueInfo. name, arg, nestedParameter. getNestedParameterType ( ) ) ;
}
return arg;
}
如代码所示,当缺少请求参数的时候,通常我们会按照以下几个步骤进行处理。
1. 查看 namedValueInfo 的默认值,如果存在则使用它。
2. 在 @RequestParam 没有指明默认值时,会查看这个参数是否必须,如果必须,则按错误处理
判断参数是否必须的代码即为下述关键代码行:namedValueInfo. required && ! nestedParameter. isOptional ( )
很明显,若要判定一个参数是否是必须的,需要同时满足两个条件:条件 1 是 @RequestParam 指明了必须(即属性 required 为 true,实际上它也是默认值),条件 2 是要求 @RequestParam 标记的参数本身不是可选的。 我们可以通过 MethodParameter#isOptional 方法看下可选的具体含义:public boolean isOptional ( ) {
return ( getParameterType ( ) == Optional . class || hasNullableAnnotation ( ) ||
( KotlinDetector . isKotlinReflectPresent ( ) &&
KotlinDetector . isKotlinType ( getContainingClass ( ) ) &&
KotlinDelegate . isOptional ( this ) ) ) ;
}
在不使用 Kotlin 的情况下,所谓可选,就是参数的类型为 Optional,或者任何标记了注解名为 Nullable 且 RetentionPolicy 为 RUNTIM 的注解。
3. 如果不是必须,则按 null 去做具体处理
解决办法
通过案例解析,我们很容易就能修正这个问题,就是让参数有默认值或为非可选即可,具体方法包含以下几种。
1. 设置 @RequestParam 的默认值
@RequestParam ( value = "address" , defaultValue = "no address" ) String address
2. 设置 @RequestParam 的 required 值
@RequestParam ( value = "address" , required = false ) String address)
3. 标记任何名为 Nullable 且 RetentionPolicy 为 RUNTIME 的注解
@RequestParam ( value = "address" ) @Nullable String address
4. 修改参数类型为 Optional
@RequestParam ( value = "address" ) Optional address
从这些修正方法不难看出:假设你不学习源码,解决方法就可能只局限于一两种,但是深入源码后,解决方法就变得格外多了。这里要特别强调的是:在 Spring Web 中,默认情况下,请求参数是必选项。