一. 介绍
在实际应用中,处理器方法的参数各式各样,遇到 Optional< Boolean > 和 Pageable 参数,SpringBoot 是如何进行解析的呢?
@GetMaping("/test01/{id}")
public RestOpResult index(
@PathVariable("endpointId") Integer endpointId,
@RequestParam(name = "uri", required = false) String uri,
@RequestParam(name = "authorized", required = false) String authorized,
@RequestHeader(name = "x-auth-token") String authToken,
@RequestHeader(name = "x-auth-groups") String authGroup,
@Parameter(hidden = true)
@RequestParam(name = "includeToken", required = false) Optional<Boolean> includeTokenParam,
Pageable pageable,
HttpServletRequest request) {
}
参数 5 和参数 6 对应的参数是 Optional< Boolean > 和 Pageable 类型,下面我们进到 SpringBoot 参数解析部分看看这俩特殊参数是如何进行解析的;
二. 参数解析
先进到 DispatcherServlet 类,直接在 invokeForRequest() 上打断点;
// -------------------------------- InvocableHandlerMethod ---------------------------------
public Object invokeForRequest(NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 进行参数解析获取参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
// -------------------------------- InvocableHandlerMethod ---------------------------------
protected Object[] getMethodArgumentValues(NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
// 1. 轮询解析参数 parameters
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 2. 看参数解析器们是否支持解析参数 parameter
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 3. 参数解析器支持解析的情况下,进行参数解析
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
throw ex;
}
}
// 返回解析出来的参数
return args;
}
1. Optional < Boolean > 参数类型
对该参数类型,使用的参数解析器是 RequestParamMethodArgumentResolver,我们看下参数解析过程;
// ---------------------------- RequestParamMethodArgumentResolver -----------------------------
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 resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 1. 获取参数值,从 request 中通过 request.getParameter() 获取参数值
// 如果没有带值,arg == null
// 如果带了值,值为 "true",arg == "true"
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
// 1.1 参数自带了默认值,即 @RequetParam 注解中带了 defaultValue 属性
if (namedValueInfo.defaultValue != null) {
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
// 1.2 参数为必须项并且参数不是 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 = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
// 2. 根据参数绑定器和converter去转换参数并绑定到参数上
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException();
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException();
}
// Check for null value after conversion of incoming argument value
if (arg == null && namedValueInfo.defaultValue == null &&
namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
}
}
// 3. 参数解析的后置操作,一般不进行啥操作
// 只有 PathVariableMethodArgumentResolver 会进行操作,会把解析出来的参数放到 request 的属性中
// 感兴趣可以看看
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
// 4. 返回解析并绑定后的参数
return arg;
}
第 2 步中,这个 binder 对象如下,它最重要的属性是内部的 conversionService 对象,coversionService 内部的 converters 包含 128 个参数转换器 converter;
当我们不传参数时,解析出来的 arg 是 Optional.empty();
当我们传 true 时,解析出来的 arg 是 Optional.of(true);
后续了解即可!!!!假设传值为 true,解析过程如下,我们直接看 GenericConversionService 类;(WebConversionService 是 GenericConversionService 的实现类)
// -------------------------------- GenericConversionService ------------------------------
public Object convert(@Nullable Object source,
@Nullable TypeDescriptor sourceType,
TypeDescriptor targetType) {
// source 值为 "true"
// sourceType 值为 java.lang.String
// targetType 值为 java.util.Optional<java.lang.Boolean>
if (sourceType == null) {
Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
return handleResult(null, targetType, convertNullSource(null, targetType));
}
if (source != null && !sourceType.getObjectType().isInstance(source)) {
throw new IllegalArgumentException();
}
// 1. 获取合适的 converter
// 这里获取到的 converter 为 ObjectToOptionalConverter
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
// 2. 根据获取到的 converter 去转换 source 值
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
看 ObjectToOptionalConverter 是如何 converter source 值的;
// --------------------------- ObjectToOptionalConverter ------------------------------
public Object convert(@Nullable Object source,
TypeDescriptor sourceType,
TypeDescriptor targetType) {
// 1. 如果为 null 值,返回 Optional.empty()
if (source == null) {
return Optional.empty();
}
// 2. 如果值就是 Optional 类型,直接返回 source 对象
else if (source instanceof Optional) {
return source;
}
// 3. 如果 Optional 具有泛型,需要进一步解析泛型
else if (targetType.getResolvableType().hasGenerics()) {
// 这里我们 target 是 Optional<Boolean> 类型
// new GenericTypeDescriptor(targetType) 得到的是 java.lang.Boolean 类型的 TypeDescriptor
// 也就是再次调用 GenericConversionService.convert() 解析出泛型对应的 target 值
// 最后返回 Optional.of(target)
// 有点递归调用的思想
Object target = this.conversionService.convert(source, sourceType,
new GenericTypeDescriptor(targetType));
if (target == null || (target.getClass().isArray() && Array.getLength(target) == 0) ||
(target instanceof Collection<?> collection && collection.isEmpty())) {
return Optional.empty();
}
return Optional.of(target);
}
else {
return Optional.of(source);
}
}
至此,Optional< Boolean > 类型的参数解析完毕;
2. Pageable 参数类型
Pageable 是 SpringData 里的对象,对于该参数类型,使用的参数解析器不是普通的 RequestParamMethodArgumentResolver,而是 PageableHandlerMethodArgumentResolver;
我们也是直接看 PageableHandlerMethodArgumentResolver 的 resolveArgument();
// ------------------------- PageableHandlerMethodArgumentResolver ---------------------------
public Pageable resolveArgument(MethodParameter methodParameter,
@Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
@Nullable WebDataBinderFactory binderFactory) {
// 1. 从 request 中获取参数 page 和 pageSize 属性
// 感兴趣可以看看
// 这里的 getPageParameterName() 和 getSizeParameterName() 默认是取 page 和 pageSize 属性
// 但是这俩属性是可以进行配置的,比如我配置成取 pageNumber 和 pageSize 的属性
String page = webRequest.getParameter(getParameterNameToUse(getPageParameterName(), methodParameter));
String pageSize = webRequest.getParameter(getParameterNameToUse(getSizeParameterName(), methodParameter));
// 2. 通过参数解析器 SortArgumentResolver#resolveArgument() 解析出 sort 对象
Sort sort = sortResolver.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory);
// 3. 根据参数,参数值等构造出 pageable 对象
Pageable pageable = getPageable(methodParameter, page, pageSize);
if (sort.isSorted()) {
return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
}
// 4. 返回该 pageable 对象
return pageable;
}
// ------------------------- PageableHandlerMethodArgumentResolver ---------------------------
protected Pageable getPageable(MethodParameter methodParameter,
@Nullable String pageString,
@Nullable String pageSizeString) {
assertPageableUniqueness(methodParameter);
// 1. 根据 methodParameter 获取默认的分页对象
Optional<Pageable> defaultOrFallback = getDefaultFromAnnotationOrFallback(methodParameter).toOptional();
Optional<Integer> page = parseAndApplyBoundaries(pageString, Integer.MAX_VALUE, true);
Optional<Integer> pageSize = parseAndApplyBoundaries(pageSizeString, maxPageSize, false);
if (!(page.isPresent() && pageSize.isPresent()) && !defaultOrFallback.isPresent()) {
return Pageable.unpaged();
}
// 2. page 参数取值,如果不带 page 参数时取默认分页参数对象的 pageNumber 值
int p = page.orElseGet(() -> defaultOrFallback.map(Pageable::getPageNumber)
.orElseThrow(IllegalStateException::new));
// pageSize 参数取值,如果不带 pageSize 参数时取默认分页对象的 pageSize 值
int ps = pageSize.orElseGet(() -> defaultOrFallback.map(Pageable::getPageSize)
.orElseThrow(IllegalStateException::new));
// 参数边界
ps = ps < 1 ? defaultOrFallback.map(Pageable::getPageSize)
.orElseThrow(IllegalStateException::new) : ps;
ps = ps > maxPageSize ? maxPageSize : ps;
// 3. 返回 Page 对象
return PageRequest.of(p, ps, defaultOrFallback.map(Pageable::getSort).orElseGet(Sort::unsorted));
}
可以看到,如果 page 和 pageSize 传了值的情况下,取传的值;
如果没有传值,取的是默认分页对象,我们看下第 1 步的默认分页对象是怎么生成的;
// ------------------------- PageableHandlerMethodArgumentResolver ---------------------------
private Pageable getDefaultFromAnnotationOrFallback(MethodParameter methodParameter) {
// 1. 获取参数 Pageable 前的 @PageableDefault 注解
MergedAnnotation<PageableDefault> defaults = MergedAnnotations.from(
methodParameter.getParameterAnnotations()).get(PageableDefault.class);
// 如果有 @PageableDefault 注解的话,根据注解的 page 和 pageSize 生成 Pageable 对象
if (defaults.isPresent()) {
return getDefaultPageRequestFrom(methodParameter, defaults);
}
// 如果没有 @PageableDefault 注解的话,使用默认值为 PageRequest.of(0, 20)
// 可以看到,如果在 request 中不带参数,Pageable 会使用默认值 PageRequest.of(0, 20)
return fallbackPageable;
}
至此,Pageable 参数解析完成;