# 精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver # 1. 概述 本文接 [《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》](http://svip.iocoder.cn/Spring-MVC/HandlerAdapter-2-ServletInvocableHandlerMethod) 一文,我们来分享 HandlerMethodArgumentResolver ,HandlerMethod 的参数解析器接口。代码如下: ``` // HandlerMethodArgumentResolver.java // HandlerMethodArgumentResolver.java public interface HandlerMethodArgumentResolver { /** * 是否支持解析该参数 * * Whether the given {@linkplain MethodParameter method parameter} is * supported by this resolver. * @param parameter the method parameter to check * @return {@code true} if this resolver supports the supplied parameter; * {@code false} otherwise */ boolean supportsParameter(MethodParameter parameter); /** * 解析该参数 * * Resolves a method parameter into an argument value from a given request. * A {@link ModelAndViewContainer} provides access to the model for the * request. A {@link WebDataBinderFactory} provides a way to create * a {@link WebDataBinder} instance when needed for data binding and * type conversion purposes. * @param parameter the method parameter to resolve. This parameter must * have previously been passed to {@link #supportsParameter} which must * have returned {@code true}. * @param mavContainer the ModelAndViewContainer for the current request * @param webRequest the current request * @param binderFactory a factory for creating {@link WebDataBinder} instances * @return the resolved argument value, or {@code null} if not resolvable * @throws Exception in case of errors with the preparation of argument values */ @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; } ``` - 两个方法,分别是是否支持解析该参数、以及解析该参数。 # 2. 类图 HandlerMethodArgumentResolver 的实现类非常多,如下图所示:[![HandlerAdapter 类图](16-Spring MVC 源码解析-HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver.assets/01.png)](http://static.iocoder.cn/images/Spring/2022-03-26/01.png)HandlerAdapter 类图 因为实在太大,胖友可以点击 [传送](http://static.iocoder.cn/images/Spring/2022-03-26/01.png) 查看。 下面,我要说什么化,想必熟悉我的胖友已经知道了,我们就分析几个 HandlerMethodArgumentResolver 实现类。哈哈哈哈 # 3. HandlerMethodArgumentResolverComposite `org.springframework.web.method.support.HandlerMethodArgumentResolverComposite` ,实现 HandlerMethodArgumentResolver 接口,复合的 HandlerMethodArgumentResolver 实现类。 ## 3.1 构造方法 ``` // HandlerMethodArgumentResolverComposite.java /** * HandlerMethodArgumentResolver 数组 */ private final List argumentResolvers = new LinkedList<>(); /** * MethodParameter 与 HandlerMethodArgumentResolver 的映射,作为缓存。 */ private final Map argumentResolverCache = new ConcurrentHashMap<>(256); ``` - `argumentResolvers` 属性,HandlerMethodArgumentResolver 数组。这就是 Composite 复合~ - `argumentResolverCache` 属性,MethodParameter 与 HandlerMethodArgumentResolver 的映射,作为**缓存**。因为,MethodParameter 是需要从 `argumentResolvers` 遍历到适合其的解析器,通过缓存后,无需再次重复遍历。 另外,在 [《精尽 Spring MVC 源码解析 —— HandlerAdapter 组件(一)之 HandlerAdapter》](http://svip.iocoder.cn/Spring-MVC/HandlerAdapter-1-HandlerAdapter) 的 [「7.2.2 getDefaultArgumentResolvers」](https://svip.iocoder.cn/Spring-MVC/HandlerAdapter-3-HandlerMethodArgumentResolver/#) 中,我们已经看到了,HandlerMethodArgumentResolverComposite 默认复合的所有 HandlerMethodArgumentResolver 对象。😈 忘记的胖友,可以点下 [传送门](https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java#L637-L682) 再瞅瞅。 ## 3.2 getArgumentResolver `#getArgumentResolver(MethodParameter parameter)` 方法,获得方法参数对应的 HandlerMethodArgumentResolver 对象。代码如下: ``` // HandlerMethodArgumentResolverComposite.java /** * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter. */ @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { // 优先从 argumentResolverCache 缓存中,获得 parameter 对应的 HandlerMethodArgumentResolver 对象 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { // 获得不到,则遍历 argumentResolvers 数组,逐个判断是否支持。 for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { // 如果支持,则添加到 argumentResolverCache 缓存中,并返回 if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; } ``` ## 3.3 supportsParameter 实现 `#supportsParameter(MethodParameter parameter)` 方法,如果能获得到对应的 HandlerMethodArgumentResolver 处理器,则说明支持。代码如下: ``` // HandlerMethodArgumentResolverComposite.java @Override public boolean supportsParameter(MethodParameter parameter) { return (getArgumentResolver(parameter) != null); } ``` ## 3.4 resolveArgument `#resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)` 方法,解析指定参数的值。代码如下: ``` // HandlerMethodArgumentResolverComposite.java @Override @Nullable public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 获得 HandlerMethodArgumentResolver 对象 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); // 如果获得不到,抛出 IllegalArgumentException 异常 if (resolver == null) { throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]"); } // 执行解析 return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } ``` # 4. AbstractNamedValueMethodArgumentResolver `org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver` ,实现 ValueMethodArgumentResolver 接口,基于名字获取值的HandlerMethodArgumentResolver 抽象基类。例如说,`@RequestParam(value = "username")` 注解的参数,就是从请求中获得 `username` 对应的参数值。😈 明白了么? AbstractNamedValueMethodArgumentResolver 的子类不多,如下图所示:[![AbstractNamedValueMethodArgumentResolver 类图](16-Spring MVC 源码解析-HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver.assets/02.png)](http://static.iocoder.cn/images/Spring/2022-03-26/02.png)AbstractNamedValueMethodArgumentResolver 类图 😝 虽然不多,但是我们仅仅分析常用的,分别是: - RequestParamMethodArgumentResolver ,基于 `@RequestParam` 注解( 也可不加该注解的请求参数 )的方法参数,在 [「5. RequestParamMethodArgumentResolver」](https://svip.iocoder.cn/Spring-MVC/HandlerAdapter-3-HandlerMethodArgumentResolver/#) 中,详细解析。 - PathVariableMethodArgumentResolver ,基于 `@PathVariable` 注解的方法参数,在 [「6. PathVariableMethodArgumentResolver」](https://svip.iocoder.cn/Spring-MVC/HandlerAdapter-3-HandlerMethodArgumentResolver/#) 中,详细解析。 ## 4.1 构造方法 ``` // AbstractNamedValueMethodArgumentResolver.java @Nullable private final ConfigurableBeanFactory configurableBeanFactory; @Nullable private final BeanExpressionContext expressionContext; /** * MethodParameter 和 NamedValueInfo 的映射,作为缓存。 */ private final Map namedValueInfoCache = new ConcurrentHashMap<>(256); public AbstractNamedValueMethodArgumentResolver() { this.configurableBeanFactory = null; this.expressionContext = null; } public AbstractNamedValueMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory) { this.configurableBeanFactory = beanFactory; this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, new RequestScope()) : null); } ``` - `namedValueInfoCache` 属性,MethodParameter 和 NamedValueInfo 的映射,作为**缓存**。 ## 4.2 NamedValueInfo NamedValueInfo ,是 AbstractNamedValueMethodArgumentResolver 的静态类,代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java /** * Represents the information about a named value, including name, whether it's required and a default value. */ protected static class NamedValueInfo { /** * 名字 */ private final String name; /** * 是否必填 */ private final boolean required; /** * 默认值 */ @Nullable private final String defaultValue; public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) { this.name = name; this.required = required; this.defaultValue = defaultValue; } } ``` ## 4.3 getNamedValueInfo `#getNamedValueInfo(MethodParameter parameter)` 方法,获得方法参数对应的 NamedValueInfo 对象。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java private NamedValueInfo getNamedValueInfo(MethodParameter parameter) { // <1> 从 namedValueInfoCache 缓存中,获得 NamedValueInfo 对象 NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter); if (namedValueInfo == null) { // <2> 获得不到,则创建 namedValueInfo 对象。这是一个抽象方法,子类来实现 namedValueInfo = createNamedValueInfo(parameter); // <3> 更新 namedValueInfo 对象 namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo); // <4> 添加到 namedValueInfoCache 缓存中 this.namedValueInfoCache.put(parameter, namedValueInfo); } return namedValueInfo; } ``` - `<1>` 处,从 `namedValueInfoCache` 缓存中,获得 NamedValueInfo 对象。 - `<2>` 处,获得不到,则调用 `#createNamedValueInfo(MethodParameter parameter)` 方法,创建 namedValueInfo 对象。这是一个**抽象**方法,子类来实现。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java /** * Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically * retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}. * @param parameter the method parameter * @return the named value information */ protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter); ``` - `<3>` 处,调用 `#updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info)` 方法,更新 `namedValueInfo` 对象。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java /** * Create a new NamedValueInfo based on the given NamedValueInfo with sanitized values. */ private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) { String name = info.name; // 如果名字为空,则抛出 IllegalArgumentException 异常 if (info.name.isEmpty()) { name = parameter.getParameterName(); // 【注意!!!】如果 name 为空,则使用参数名 if (name == null) { throw new IllegalArgumentException( "Name for argument type [" + parameter.getNestedParameterType().getName() + "] not available, and parameter name information not found in class file either."); } } // 获得默认值 String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue); // 创建 NamedValueInfo 对象 return new NamedValueInfo(name, info.required, defaultValue); } ``` - 最终,会创建一个**新的** NamedValueInfo 对象。 - `<4>` 处,添加到 `namedValueInfoCache` 缓存中。 ## 4.4 resolveArgument `#resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)` 方法,解析指定参数的值。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java @Override @Nullable public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // <1> 获得方法参数对应的 NamedValueInfo 对象。 NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); // <2> 如果 parameter 是内嵌类型的,则获取内嵌的参数。否则,还是使用 parameter 自身 MethodParameter nestedParameter = parameter.nestedIfOptional(); // <3> 如果 name 是占位符,则进行解析成对应的值 Object resolvedName = resolveStringValue(namedValueInfo.name); if (resolvedName == null) { // 如果解析不到,则抛出 IllegalArgumentException 异常 throw new IllegalArgumentException( "Specified name must not resolve to null: [" + namedValueInfo.name + "]"); } // <4> 解析 name 对应的值 Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest); // <5> 如果 arg 不存在,则使用默认值 if (arg == null) { if (namedValueInfo.defaultValue != null) { // <5.1> 使用默认值 arg = resolveStringValue(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { // <5.2> 如果是必填,则处理参数缺失的情况 handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); } // <5.3> 处理空值的情况 arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType()); // <6> 如果 arg 为空串,则使用默认值 } else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveStringValue(namedValueInfo.defaultValue); } // <7> 执行值的类型转换 if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); try { arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter); } catch (ConversionNotSupportedException ex) { throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } catch (TypeMismatchException ex) { throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } } // 处理解析的值 handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; } ``` - `<1>` 处,调用 `#getNamedValueInfo(MethodParameter parameter)` 方法,获得方法参数对应的 NamedValueInfo 对象。也就是说,在 [「4.3 getNamedValueInfo」](https://svip.iocoder.cn/Spring-MVC/HandlerAdapter-3-HandlerMethodArgumentResolver/#) 中。 - `<2>` 处,如果 `parameter` 是内嵌类型的,则获取内嵌的参数。否则,还是使用 `parameter` 自身。一般情况下,`parameter` 参数,我们不太会使用 Optional 类型。所以,胖友也可以不理解。 - `<3>` 处,调用 `#resolveStringValue(String value)` 方法,如果 `name` 是占位符,则进行解析成对应的值。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java @Nullable private Object resolveStringValue(String value) { // 如果 configurableBeanFactory 为空,则不进行解析 if (this.configurableBeanFactory == null) { return value; } // 如果 exprResolver 或 expressionContext 为空,则不进行解析 BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver(); if (exprResolver == null || this.expressionContext == null) { return value; } // 获得占位符对应的值 String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value); // 计算表达式 return exprResolver.evaluate(placeholdersResolved, this.expressionContext); } ``` - 这种用法非常小众,艿艿重来没用过。示例如下: ``` // Controller.java @RequestMapping("/hello3") public String hello3(@RequestParam(value = "${server.port}") String name) { return "666"; } // application.properties server.port=8012 ``` - 此时,就可以 `GET /hello3?8012=xxx` 。 - `<4>` 处,调用 `#resolveName(String name, MethodParameter parameter, NativeWebRequest request)` **抽象**方法,解析 `name` 对应的值。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java @Nullable protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception; ``` - `<5>` 处,如果 `arg` 为 `null` ,则使用默认值。 - `<5.1>` 处,如果默认值非空,则调用 `#resolveStringValue(defaultValue)` 方法,解析默认值。 - `<5.2>` 处,如果是必填,则调用 `#handleMissingValue(handleMissingValue)` 方法,处理参数缺失的情况调用。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { handleMissingValue(name, parameter); } protected void handleMissingValue(String name, MethodParameter parameter) throws ServletException { throw new ServletRequestBindingException("Missing argument '" + name + "' for method parameter of type " + parameter.getNestedParameterType().getSimpleName()); } ``` - 抛出 ServletRequestBindingException 异常。 - 另外,子类 RequestParamMethodArgumentResolver 会重写该方法。 - `<5.3>` 处,调用 `#handleNullValue(String name, Object value, Class paramType)` 方法,处理 `null` 值的情况。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java /** * A {@code null} results in a {@code false} value for {@code boolean}s or an exception for other primitives. */ @Nullable private Object handleNullValue(String name, @Nullable Object value, Class paramType) { if (value == null) { // 如果是 Boolean 类型,则返回 FALSE if (Boolean.TYPE.equals(paramType)) { return Boolean.FALSE; // 如果基本类型,因为 null 无法转化,则抛出 IllegalStateException 异常 } else if (paramType.isPrimitive()) { throw new IllegalStateException("Optional " + paramType.getSimpleName() + " parameter '" + name + "' is present but cannot be translated into a null value due to being declared as a " + "primitive type. Consider declaring it as object wrapper for the corresponding primitive type."); } } // 返回默认值 return value; } ``` - 牛逼,各种细节的处理啊。 - `<6>` 处,逻辑同 `<5.1>` 处。 - `<7>` 处,执行值的类型转换。TODO 1016 - `<8>` 处,调用 `#handleResolvedValue(Object arg, String name, MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)` 方法,解析值后的后置处理。代码如下: ``` // AbstractNamedValueMethodArgumentResolver.java protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest) { } ``` - 默认为空方法。 - 另外,子类 PathVariableMethodArgumentResolver 会重写该方法。 > 艿艿:代码有丢丢长,实际逻辑是不复杂的。胖友在回看下。 # 5. RequestParamMethodArgumentResolver `org.springframework.web.method.annotation.RequestParamMethodArgumentResolver` ,实现 UriComponentsContributor 接口,继承 AbstractNamedValueMethodArgumentResolver 抽象类,请求参数的 HandlerMethodArgumentResolver 实现类,处理**普通的请求参数**。 ## 5.1 构造方法 ``` // RequestParamMethodArgumentResolver.java private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); /** * 是否使用默认解决。 * * 这个变量有点绕,见 {@link #supportsParameter(MethodParameter)} 方法 */ private final boolean useDefaultResolution; public RequestParamMethodArgumentResolver(boolean useDefaultResolution) { this.useDefaultResolution = useDefaultResolution; } public RequestParamMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory, boolean useDefaultResolution) { super(beanFactory); this.useDefaultResolution = useDefaultResolution; } ``` ## 5.2 supportsParameter 实现 `#supportsParameter(MethodParameter parameter)` 方法,代码如下: ``` // RequestParamMethodArgumentResolver.java @Override public boolean supportsParameter(MethodParameter parameter) { // 有 @RequestParam 注解的情况 if (parameter.hasParameterAnnotation(RequestParam.class)) { // <3> 如果是 Map 类型,则 @RequestParam 注解必须要有 name 属性 if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && StringUtils.hasText(requestParam.name())); // 其它,默认支持 } else { return true; } } else { // 如果有 @RequestPart 注解,返回 false 。即 @RequestPart 的优先级 > @RequestParam if (parameter.hasParameterAnnotation(RequestPart.class)) { return false; } // 获得参数,如果存在内嵌的情况 parameter = parameter.nestedIfOptional(); // <1> 如果 Multipart 参数。则返回 true ,表示支持 if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { return true; // <2> 如果开启 useDefaultResolution 功能,则判断是否为普通类型 } else if (this.useDefaultResolution) { return BeanUtils.isSimpleProperty(parameter.getNestedParameterType()); // 其它,不支持 } else { return false; } } } ``` - 情况有点多,胖友耐心看下注释。我们只讲里面的几处调用。 - `<1>` 处,调用 `MultipartResolutionDelegate#isMultipartArgument(parameter)` 方法,如果 Multipart 参数。则返回 `true` ,表示支持。代码如下: ``` // MultipartResolutionDelegate.java public static boolean isMultipartArgument(MethodParameter parameter) { Class paramType = parameter.getNestedParameterType(); // MultipartFile 的多种情况 return (MultipartFile.class == paramType || isMultipartFileCollection(parameter) || isMultipartFileArray(parameter) || // Part 的多种情况 (Part.class == paramType || isPartCollection(parameter) || isPartArray(parameter))); } ``` - 上传文件相关的类型。 - `<2>` 处,如果开启 `useDefaultResolution` 功能,则调用 `BeanUtils#isSimpleProperty(Class clazz)` 方法,判断是否为普通类型。代码如下: ``` // BeanUtils.java public static boolean isSimpleProperty(Class clazz) { Assert.notNull(clazz, "Class must not be null"); return isSimpleValueType(clazz) || (clazz.isArray() && isSimpleValueType(clazz.getComponentType())); } public static boolean isSimpleValueType(Class clazz) { return (ClassUtils.isPrimitiveOrWrapper(clazz) || Enum.class.isAssignableFrom(clazz) || CharSequence.class.isAssignableFrom(clazz) || Number.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz) || URI.class == clazz || URL.class == clazz || Locale.class == clazz || Class.class == clazz); } ``` - 继续补充 `<2>` 处,那么 `useDefaultResolution` 到底是怎么样的赋值呢?回到 RequestMappingHandlerAdapter 的 `#getDefaultArgumentResolvers()` 的方法,精简代码如下: ``` // RequestMappingHandlerAdapter.java private List getDefaultArgumentResolvers() { List resolvers = new ArrayList<>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); // .. 省略一大片 // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); // .. 省略几个 return resolvers; } ``` - 我们可以看到有两个 RequestParamMethodArgumentResolver 对象,前者 `useDefaultResolution` 为 `false` ,后者为 `useDefaultResolution` 为 `true` 。什么意思呢?优先将待有 `@RequestParam` 注解的请求参数给第一个 RequestParamMethodArgumentResolver 对象;其次,给中间省略的一大片参数解析器试试能不能解析;最后,使用第二个 RequestParamMethodArgumentResolver 兜底,处理剩余的情况。 - `<3>` 处,如果是 Map 类型,则 @RequestParam 注解必须要有 `name` 属性。是不是感觉有几分灵异?答案在 [「6. RequestParamMapMethodArgumentResolver」](https://svip.iocoder.cn/Spring-MVC/HandlerAdapter-3-HandlerMethodArgumentResolver/#) 一起讲。 ## 5.3 createNamedValueInfo `#createNamedValueInfo(MethodParameter parameter)` 方法,创建 NamedValueInfo 对象。代码如下: ``` // RequestParamMethodArgumentResolver.java @Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { RequestParam ann = parameter.getParameterAnnotation(RequestParam.class); return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); } private static class RequestParamNamedValueInfo extends NamedValueInfo { public RequestParamNamedValueInfo() { super("", false, ValueConstants.DEFAULT_NONE); } public RequestParamNamedValueInfo(RequestParam annotation) { super(annotation.name(), annotation.required(), annotation.defaultValue()); } } ``` - 虽然,在无 `@RequestMapping` 时,返回的 RequestParamNamedValueInfo 对象的 `name` 属性为 `""` ,但是胖友在回过头看 [「4.3 getNamedValueInfo」](https://svip.iocoder.cn/Spring-MVC/HandlerAdapter-3-HandlerMethodArgumentResolver/#) ,有一个“【注意】!!!”的地方。😈 ## 5.4 resolveName 实现 `#resolveName(String name, MethodParameter parameter, NativeWebRequest request)` 方法,获得参数的值。代码如下: ``` // RequestParamMethodArgumentResolver.java @Override @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { // 情况一,HttpServletRequest 情况下的 MultipartFile 和 Part 的情况 HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); if (servletRequest != null) { Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest); if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) { return mpArg; } } // 情况二,MultipartHttpServletRequest 情况下的 MultipartFile 的情况 Object arg = null; MultipartHttpServletRequest multipartRequest = request.getNativeRequest(MultipartHttpServletRequest.class); if (multipartRequest != null) { List files = multipartRequest.getFiles(name); if (!files.isEmpty()) { arg = (files.size() == 1 ? files.get(0) : files); } } // 情况三,普通参数的获取 if (arg == null) { String[] paramValues = request.getParameterValues(name); if (paramValues != null) { arg = (paramValues.length == 1 ? paramValues[0] : paramValues); } } return arg; } ``` - 情况一、二,是处理参数类型为**文件** `org.springframework.web.multipart.MultipartFile` 和 `javax.servlet.http.Part` 的参数的获取。 - 情况三,是处理**普通**参数的获取。就是我们常见的 String、Integer 之类的请求参数。 ## 5.5 handleMissingValue 重写 `#handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request)` 方法,代码如下: ``` // RequestParamMethodArgumentResolver.java @Override protected void handleMissingValue(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { if (servletRequest == null || !MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { throw new MultipartException("Current request is not a multipart request"); } else { throw new MissingServletRequestPartException(name); } } else { throw new MissingServletRequestParameterException(name, parameter.getNestedParameterType().getSimpleName()); } } ``` - 根据参数的类型,做更详细的异常抛出。 # 6. RequestParamMapMethodArgumentResolver `org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver` ,实现 HandlerMethodArgumentResolver 接口,处理带有 `@RequestParam` 注解,但是注解上无 `name` 属性的 Map 类型的参数的 RequestParamMethodArgumentResolver 实现类。代码如下: ``` // RequestParamMapMethodArgumentResolver.java public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) && !StringUtils.hasText(requestParam.name())); } @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Class paramType = parameter.getParameterType(); // 获得请求的参数集合 Map parameterMap = webRequest.getParameterMap(); // MultiValueMap 类型的处理 if (MultiValueMap.class.isAssignableFrom(paramType)) { MultiValueMap result = new LinkedMultiValueMap<>(parameterMap.size()); parameterMap.forEach((key, values) -> { for (String value : values) { result.add(key, value); } }); return result; // 普通 Map 类型的处理 } else { Map result = new LinkedHashMap<>(parameterMap.size()); parameterMap.forEach((key, values) -> { if (values.length > 0) { result.put(key, values[0]); } }); return result; } } } ``` - 具体的代码实现,胖友自己瞅瞅。当然,良心如艿艿,还是会提供示例。 ① 对于 RequestParamMapMethodArgumentResolver 类,它的效果是,将所有参数添加到 Map 集合中。示例如下: ``` // Controller.java @RequestMapping("/hello4") public String hello4(@RequestParam Map map) { return "666"; } ``` - `GET /hello4?name=yyy&age=20` 的 `name` 和 `age` 参数,就会都添加到 `map` 中。 ② 对于 RequestParamMethodArgumentResolver 类,它的效果是,将指定名字的参数添加到 Map 集合中。示例如下: ``` // Controller.java @RequestMapping("/hello5") public String hello5(@RequestParam(name = "map") Map map) { return "666"; } ``` - `GET /hello4?map={"name": "yyyy", age: 20}` 的 `map` 参数,就会都添加到 `map` 中。当然,要注意下,实际请求要 UrlEncode 编码下参数,所以实际请求是 `GET /hello4?map=%7b%22name%22%3a+%22yyyy%22%2c+age%3a+20%7d` 。 # 7. PathVariableMethodArgumentResolver `org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver` ,实现 UriComponentsContributor 接口,继承 AbstractNamedValueMethodArgumentResolver 抽象类,处理路径参数。 ## 7.1 supportsParameter 实现 `#supportsParameter(MethodParameter parameter)` 方法,代码如下: ``` // PathVariableMethodArgumentResolver.java @Override public boolean supportsParameter(MethodParameter parameter) { // 如果无 @PathVariable 注解 if (!parameter.hasParameterAnnotation(PathVariable.class)) { return false; } // Map 类型,有 @PathVariable 注解,但是有 name 属性 if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class); return (pathVariable != null && StringUtils.hasText(pathVariable.value())); } // 有 @PathVariable 注解 return true; } ``` - 大体逻辑,胖友自己看代码。 - `` 处,又出了上面刚刚说过的 Map 的情况,胖友自己去对比 `org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver` 类。 ## 7.2 createNamedValueInfo 实现 `#createNamedValueInfo(MethodParameter parameter)` 方法,代码如下: ``` // PathVariableMethodArgumentResolver.java @Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { // 获得 @PathVariable 注解 PathVariable ann = parameter.getParameterAnnotation(PathVariable.class); Assert.state(ann != null, "No PathVariable annotation"); // 创建 PathVariableNamedValueInfo 对象 return new PathVariableNamedValueInfo(ann); } private static class PathVariableNamedValueInfo extends NamedValueInfo { public PathVariableNamedValueInfo(PathVariable annotation) { super(annotation.name(), annotation.required(), ValueConstants.DEFAULT_NONE); } } ``` ## 7.3 resolveName 实现 `#resolveName(String name, MethodParameter parameter, NativeWebRequest request)` 方法,代码如下: ``` // PathVariableMethodArgumentResolver.java @Override @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { // 获得路径参数 Map uriTemplateVars = (Map) request.getAttribute( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); // 获得参数值 return (uriTemplateVars != null ? uriTemplateVars.get(name) : null); } ``` ## 7.4 handleMissingValue 重写 `#handleMissingValue()` 方法,抛出 MissingPathVariableException 异常。代码如下: ``` // PathVariableMethodArgumentResolver.java @Override protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException { // 抛出 MissingPathVariableException 异常 throw new MissingPathVariableException(name, parameter); } ``` ## 7.5 handleResolvedValue 重写 `#handleResolvedValue(Object arg, String name, MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request)` 方法,添加获得的属性值到请求的 `View.PATH_VARIABLES` 属性种。代码如下: ``` // PathVariableMethodArgumentResolver.java @Override protected void handleResolvedValue(@Nullable Object arg, String name, MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest request) { // 获得 pathVars String key = View.PATH_VARIABLES; int scope = RequestAttributes.SCOPE_REQUEST; Map pathVars = (Map) request.getAttribute(key, scope); // 如果不存在 pathVars ,则进行创建 if (pathVars == null) { pathVars = new HashMap<>(); request.setAttribute(key, pathVars, scope); } // 添加 name + arg 到 pathVars 中 pathVars.put(name, arg); } ``` # 666. 彩蛋 感觉,还有一些需要写的 HandlerMethodArgumentResolver 实现类,暂时还没想好。 如果胖友有什么 HandlerMethodArgumentResolver 实现类,希望艿艿来写,请在星球给我留言。TODO 9999 HandlerMethodArgumentResolver 参考和推荐如下文章: - 韩路彪 [《看透 Spring MVC:源代码分析与实践》](https://item.jd.com/11807414.html) 的 [「第 13.7 HandlerMethodArgumentResolver」](https://svip.iocoder.cn/Spring-MVC/HandlerAdapter-3-HandlerMethodArgumentResolver/#) 小节