该系列文档是本人在学习 Spring MVC 的源码过程当中总结下来的,可能对读者不太友好,请结合个人源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读html
Spring 版本:5.2.4.RELEASEjava
该系列其余文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》git
HandlerAdapter 组件,处理器的适配器。由于处理器 handler
的类型是 Object 类型,须要有一个调用者来实现 handler
是怎么被执行。Spring 中的处理器的实现多变,好比用户的处理器能够实现 Controller 接口或者 HttpRequestHandler 接口,也能够用 @RequestMapping
注解将方法做为一个处理器等,这就致使 Spring MVC 没法直接执行这个处理器。因此这里须要一个处理器适配器,由它去执行处理器github
因为 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,因此将这部份内容拆分红了五个模块,依次进行分析:web
本文是接着《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》一文来分享 HandlerMethodArgumentResolver 组件。在 HandlerAdapter
执行处理器的过程当中,具体的执行过程交由 ServletInvocableHandlerMethod
对象来完成,其中须要先经过 HandlerMethodArgumentResolver 参数解析器从请求中解析出方法的入参,而后再经过反射机制调用对应的方法。spring
先来回顾一下 ServletInvocableHandlerMethod
在哪里调用参数解析器的,能够回到 《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》 中 InvocableHandlerMethod 小节下面的 getMethodArgumentValues
方法,以下:express
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]; for (int i = 0; i < parameters.length; i++) { // 得到当前遍历的 MethodParameter 对象,并设置 parameterNameDiscoverer 到其中 MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); // <1> 先从 providedArgs 中得到参数。若是得到到,则进入下一个参数的解析,默认状况 providedArgs 不会传参 args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } // <2> 判断 resolvers 是否支持当前的参数解析 if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { // 执行解析,解析成功后,则进入下一个参数的解析 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }
<2>
处,在获取到 Method 方法的全部参数对象,依次处理,根据 resolvers
判断是否支持该参数的处理,若是支持则进行参数转换数组
resolvers
为 HandlerMethodArgumentResolverComposite 组合对象,包含了许多的参数解析器缓存
org.springframework.web.method.support.HandlerMethodArgumentResolver
,方法参数解析器mvc
public interface HandlerMethodArgumentResolver { /** * 是否支持解析该参数 */ boolean supportsParameter(MethodParameter parameter); /** * 解析该参数 */ @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; }
由于请求入参的场景很是多,因此 HandlerMethodArgumentResolver 的实现类也很是多,上面仅列出了部分实现类,本文也仅分析上面图中右侧常见的几种参数场景
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
,实现 HandlerMethodArgumentResolver 接口,复合的 HandlerMethodArgumentResolver 实现类
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { /** * HandlerMethodArgumentResolver 数组 */ private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>(); /** * MethodParameter 与 HandlerMethodArgumentResolver 的映射,做为缓存 */ private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256); }
argumentResolvers
:HandlerMethodArgumentResolver 数组。这就是 Composite 复合~argumentResolverCache
:MethodParameter 与 HandlerMethodArgumentResolver 的映射,做为缓存。由于,MethodParameter 是须要从 argumentResolvers
遍历到适合其的解析器,经过缓存后,无需再次重复遍历在《HandlerAdapter 组件(一)之 HandlerAdapter》的RequestMappingHandlerAdapter小节的 getDefaultArgumentResolvers
方法中能够看到,默认的 argumentResolvers
有哪些 HandlerMethodArgumentResolver 实现类,注意这里是有顺序的添加哦
getArgumentResolver(MethodParameter parameter)
方法,得到方法参数对应的 HandlerMethodArgumentResolver 对象,方法以下:
@Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { // 优先从 argumentResolverCache 缓存中,得到 parameter 对应的 HandlerMethodArgumentResolver 对象 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { // 得到不到,则遍历 argumentResolvers 数组,逐个判断是否支持。 for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { // 若是支持,则添加到 argumentResolverCache 缓存中,并返回 if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }
很简单,先从argumentResolverCache
缓存中获取,没有获取到则遍历 argumentResolvers
,若是支持该参数则该 HandlerMethodArgumentResolver 对象并缓存起来
注意,往 argumentResolvers
添加的顺序靠前,则优先判断是否支持该参数哦~
实现 supportsParameter(MethodParameter parameter)
方法,若是能得到到对应的 HandlerMethodArgumentResolver 参数处理器,则说明支持处理该参数,方法以下:
@Override public boolean supportsParameter(MethodParameter parameter) { return getArgumentResolver(parameter) != null; }
实现 resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
方法,解析出指定参数的值,方法以下:
@Override @Nullable public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 获取参数解析器 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first."); } /** * 进行解析 * * 基于 @RequestParam 注解 * {@link org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveArgument} * 基于 @PathVariable 注解 * {@link org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#resolveArgument} */ return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }
很简单,获取到该方法参数对应的 HandlerMethodArgumentResolver 参数处理器,而后调用其 resolveArgument
执行解析
org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver
,实现 ValueMethodArgumentResolver 接口,基于名字获取值的HandlerMethodArgumentResolver 抽象基类。例如说,@RequestParam(value = "username")
注解的参数,就是从请求中得到 username
对应的参数值。😈 明白了么?
AbstractNamedValueMethodArgumentResolver 的子类也有挺多了,咱们仅分析它的两个子类,如上面类图的下面两个:
@RequestParam
注解( 也可不加该注解的请求参数 )的方法参数,详情见下文@PathVariable
注解的方法参数,详情见下文public abstract class AbstractNamedValueMethodArgumentResolver implements HandlerMethodArgumentResolver { @Nullable private final ConfigurableBeanFactory configurableBeanFactory; @Nullable private final BeanExpressionContext expressionContext; /** * MethodParameter 和 NamedValueInfo 的映射,做为缓存 */ private final Map<MethodParameter, NamedValueInfo> namedValueInfoCache = new ConcurrentHashMap<>(256); }
AbstractNamedValueMethodArgumentResolver 的静态内部类,代码以下:
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; } }
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; }
从 namedValueInfoCache
缓存中,得到 NamedValueInfo 对象,获取到则直接返回
得到不到,则调用 createNamedValueInfo(MethodParameter parameter)
方法,建立 NamedValueInfo 对象。这是一个抽象方法,交由子类来实现
调用 updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info)
方法,更新 NamedValueInfo 对象,方法以下:
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) { String name = info.name; if (info.name.isEmpty()) { // 【注意!!!】若是 name 为空,则使用参数名 name = parameter.getParameterName(); 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 对象返回
添加到 namedValueInfoCache
缓存中
返回该 NamedValueInfo 对象
resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
方法,从请求中解析出指定参数的值
@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 是内嵌类型(Optional 类型)的,则获取内嵌的参数。不然,仍是使用 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) { // <5.1> 使用默认值 if (namedValueInfo.defaultValue != null) { arg = resolveStringValue(namedValueInfo.defaultValue); } // <5.2> 若是是必填,则处理参数缺失的状况 else if (namedValueInfo.required && !nestedParameter.isOptional()) { 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()); } } // <8> 处理解析的值 handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; }
调用 getNamedValueInfo(MethodParameter parameter)
方法,得到方法参数对应的 NamedValueInfo 对象
若是 parameter
是内嵌类型(Optional 类型)的,则获取内嵌的参数。不然,仍是使用 parameter
自身。通常状况下,parameter
参数,咱们不太会使用 Optional 类型。能够暂时忽略
调用 resolveStringValue(String value)
方法,若是 name
是占位符,则进行解析成对应的值,方法以下:
@Nullable private Object resolveStringValue(String value) { // 若是 configurableBeanFactory 为空,则不进行解析 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); }
这种用法很是小众,历来没用过。示例以下:
// Controller.java @RequestMapping("/hello3") public String hello3(@RequestParam(value = "${server.port}") String name) { return "666"; } // application.properties server.port=8012
此时,就能够发送 GET /hello3?8012=xxx
请求
【重点】调用 resolveName(String name, MethodParameter parameter, NativeWebRequest request)
抽象方法,解析参数名 name
对应的值,交由子类去实现
若是上面解析出来的参数值 arg
为 null
,则使用默认值
若是默认值非空,则调用 resolveStringValue(defaultValue)
方法,解析默认值
若是是必填,则调用 handleMissingValue(handleMissingValue)
方法,处理参数缺失的状况调用,也就是抛出指定的异常
调用 handleNullValue(String name, Object value, Class<?> paramType)
方法,处理 null
值的状况,方法以下:
@Nullable private Object handleNullValue(String name, @Nullable Object value, Class<?> paramType) { if (value == null) { if (Boolean.TYPE.equals(paramType)) { return Boolean.FALSE; } else if (paramType.isPrimitive()) { // 若是是基本类型则不能为 null 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; }
不然,若是 arg
为空字符串,而且存在默认值,则和上面的 5.1
相同处理方式
数据绑定相关,暂时忽略
调用 handleResolvedValue(Object arg, String name, MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
方法,解析参数值的后置处理,空方法,子类能够覆盖,子类 PathVariableMethodArgumentResolver 会重写该方法
代码有点长,不过逻辑不难理解
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
,实现 UriComponentsContributor 接口,继承 AbstractNamedValueMethodArgumentResolver 抽象类,参数解析器 HandlerMethodArgumentResolver 的实现类,处理普通的请求参数
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver implements UriComponentsContributor { 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; } }
实现 supportsParameter(MethodParameter parameter)
方法,判断是否支持处理该方法入参,方法以下:
@Override public boolean supportsParameter(MethodParameter parameter) { // <3> 有 @RequestParam 注解的状况 if (parameter.hasParameterAnnotation(RequestParam.class)) { // <3.1> 若是是 Map 类型,则 @RequestParam 注解必需要有 name 属性 if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && StringUtils.hasText(requestParam.name())); } else { // <3.2> 不然返回 true 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; } } }
若是 Multipart 参数。则返回 true ,表示支持调用 MultipartResolutionDelegate#isMultipartArgument(parameter)
方法,若是 Multipart 参数。则返回 true
,表示支持。代码以下:
public static boolean isMultipartArgument(MethodParameter parameter) { Class<?> paramType = parameter.getNestedParameterType(); return (MultipartFile.class == paramType || isMultipartFileCollection(parameter) || isMultipartFileArray(parameter) || (Part.class == paramType || isPartCollection(parameter) || isPartArray(parameter))); }
上传文件相关类型
若是开启 useDefaultResolution
功能,则调用 BeanUtils#isSimpleProperty(Class<?> clazz)
方法,判断是否为普通类型,代码以下:
public static boolean isSimpleProperty(Class<?> type) { Assert.notNull(type, "'type' must not be null"); return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType())); } public static boolean isSimpleValueType(Class<?> type) { return (type != void.class && type != Void.class && (ClassUtils.isPrimitiveOrWrapper(type) || Enum.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || URI.class == type || URL.class == type || Locale.class == type || Class.class == type)); }
那么 useDefaultResolution
究竟是怎么被赋值的呢?回到 RequestMappingHandlerAdapter 的 getDefaultArgumentResolvers()
的方法,精简代码以下:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); // ... 省略许多 HandlerMethodArgumentResolver 的添加 // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
咱们能够看到有两个 RequestParamMethodArgumentResolver 对象,前者 useDefaultResolution
为 false
,后者为 useDefaultResolution
为 true
。什么意思呢?优先将待有 @RequestParam
注解的请求参数给第一个 RequestParamMethodArgumentResolver 对象;其次,给中间省略的一大片参数解析器试试能不能解析;最后,使用第二个 RequestParamMethodArgumentResolver 兜底,处理剩余的状况。
若是该方法参数有 @RequestParam
注解的状况
@RequestParam
注解必需要有 name 属性,是否是感受有几分灵异?答案在下面的 RequestParamMapMethodArgumentResolver 中揭晓true
实现父类的 createNamedValueInfo(MethodParameter parameter)
方法,建立 NamedValueInfo 对象,方法以下:
@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()); } }
若是方法参数有 @RequestParam
注解,则根据注解建立一个 RequestParamNamedValueInfo 对象,获取注解中的 name
、required
和 defaultValue
配置
不然,就建立一个空的 RequestParamNamedValueInfo 对象,三个属性分别为,空字符串
、false
和 ValueConstants.DEFAULT_NONE
上面的 getNamedValueInfo 方法中讲述到,name
为 空字符串
没有关系,会获取方法的参数名
说明:经过反射获取方法的参数名,咱们只能获取到 arg0,arg1 的名称,由于jdk8以后这些变量名称没有被编译到class文件中,编译时须要指定
-parameters
选项,方法的参数名才会记录到class文件中,运行时咱们就能够经过反射机制获取到,因此咱们最好仍是用@RequestParam
注解来标注
ValueConstants.DEFAULT_NONE
则会设置为 null
实现 #resolveName(String name, MethodParameter parameter, NativeWebRequest request)
方法,得到参数的值,方法以下:
@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; MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class); if (multipartRequest != null) { List<MultipartFile> 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
的参数的获取,例如咱们经常使用到 MultipartFile 做为参数就是在这里处理的由于在《MultipartResolver 组件》中讲过了会对请求进行处理,包括解析出参数,解析成对应的 HttpServletRequest 对象
得到到参数值后,就能够准备开始经过反射调用对应的方法了
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver
,实现 HandlerMethodArgumentResolver 接口,用于处理带有 @RequestParam
注解,可是注解上没有 name
属性的 Map 类型的参数, HandlerMethodArgumentResolver 的实现类,代码以下:
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 { ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter); // MultiValueMap 类型的处理 if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) { Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve(); if (valueType == MultipartFile.class) { // MultipartFile 类型 MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest); return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0)); } else if (valueType == Part.class) { // Part 类型 HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { Collection<Part> parts = servletRequest.getParts(); LinkedMultiValueMap<String, Part> result = new LinkedMultiValueMap<>(parts.size()); for (Part part : parts) { result.add(part.getName(), part); } return result; } return new LinkedMultiValueMap<>(0); } else { Map<String, String[]> parameterMap = webRequest.getParameterMap(); MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size()); parameterMap.forEach((key, values) -> { for (String value : values) { result.add(key, value); } }); return result; } } // 普通 Map 类型的处理 else { Class<?> valueType = resolvableType.asMap().getGeneric(1).resolve(); if (valueType == MultipartFile.class) { // MultipartFile 类型 MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest); return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0)); } else if (valueType == Part.class) { // Part 类型 HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { Collection<Part> parts = servletRequest.getParts(); LinkedHashMap<String, Part> result = new LinkedHashMap<>(parts.size()); for (Part part : parts) { if (!result.containsKey(part.getName())) { result.put(part.getName(), part); } } return result; } return new LinkedHashMap<>(0); } else { Map<String, String[]> parameterMap = webRequest.getParameterMap(); Map<String, String> 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("/hello") public String hello4(@RequestParam Map<String, Object> map) { return "666"; }
发送请求 GET /hello?name=yyy&age=20
,name
和 age
参数,就会都添加到 map
中
对于 RequestParamMethodArgumentResolver 类,它的效果是,将指定名字的参数添加到 Map 集合中,示例以下:
// Controller.java @RequestMapping("/hello") public String hello5(@RequestParam(name = "map") Map<String, Object> map) { return "666"; }
发送请求 GET /hello4?map={"name": "yyyy", age: 20}
, map
参数的元素则都会添加到方法参数 map
中。固然,要注意下,实际请求要 UrlEncode 编码下参数,因此实际请求是 GET /hello?map=%7b%22name%22%3a+%22yyyy%22%2c+age%3a+20%7d
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
,实现 UriComponentsContributor 接口,继承 AbstractNamedValueMethodArgumentResolver 抽象类,处理路径参数
实现 supportsParameter(MethodParameter parameter)
方法,判断是否支持处理该方法参数,代码以下:
@Override public boolean supportsParameter(MethodParameter parameter) { // <1> 若是无 @PathVariable 注解 if (!parameter.hasParameterAnnotation(PathVariable.class)) { return false; } // <2> Map 类型,有 @PathVariable 注解,可是有 name 属性 if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class); return (pathVariable != null && StringUtils.hasText(pathVariable.value())); } return true; }
@PathVariable
注解则直接返回 fasle
,也就是说必须配置 @PathVariable
注解@PathVariable
注解有 name
属性,才返回 true
,查看 org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver
就理解了,和上述的逻辑差很少true
实现 createNamedValueInfo(MethodParameter parameter)
方法,方法以下:
@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); } }
必需要有 @PathVariable
注解,没有的话抛出异常,而后根据注解建立 PathVariableNamedValueInfo 对象
实现 resolveName(String name, MethodParameter parameter, NativeWebRequest request)
方法,从请求路径中获取方法参数的值,方法以下:
@Override @SuppressWarnings("unchecked") @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { // 得到路径参数 Map<String, String> uriTemplateVars = (Map<String, String>) request. getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); // 得到参数值 return (uriTemplateVars != null ? uriTemplateVars.get(name) : null); }
重写 handleResolvedValue(Object arg, String name, MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request)
方法,添加得到的属性值到请求的 View.PATH_VARIABLES
属性种,方法以下:
@Override @SuppressWarnings("unchecked") 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<String, Object> pathVars = (Map<String, Object>) request.getAttribute(key, scope); // 若是不存在 pathVars,则进行建立 if (pathVars == null) { pathVars = new HashMap<>(); request.setAttribute(key, pathVars, scope); } // 添加 name + arg 到 pathVars 中 pathVars.put(name, arg); }
具体用途还不清楚😈
在 HandlerAdapter
执行 HandlerMethod
处理器的过程当中,会将该处理器封装成 ServletInvocableHandlerMethod
对象,经过该对象来执行处理器。该对象经过反射机制调用对应的方法,在调用方法以前,借助 HandlerMethodArgumentResolver 参数解析器从请求中获取到对应的方法参数值,由于你没法确认哪一个参数值对应哪一个参数,因此须要先经过它从请求中解析出参数值,一一对应,而后才能调用该方法。
HandlerMethodArgumentResolver 参数解析器的实现类很是多,采用了组合模式来进行处理,若是有某一个参数解析器支持解析该方法参数,则使用它从请求体中获取到该方法参数的值,注意这里有必定的前后顺序,由于是经过 LinkedList 保存全部的实现类,排在前面的实现类则优先处理。
本文分析了咱们经常使用的 @RequestParam
和 @PathVariable
注解所对应的 HandlerMethodArgumentResolver 实现类,以下:
RequestParamMethodArgumentResolver
:解析 @RequestParam
注解配置参数(名称、是否必须、默认值),根据注解配置从请求获取参数值PathVariableMethodArgumentResolver
:解析 @PathVariable
注解配置的(名称、是否必须),根据注解配置从请求路径中获取参数值注意,关于方法参数为 Map 类型,应该如何配置,能够参考上面的 RequestParamMapMethodArgumentResolver
小节中的两个示例
关于其余的 HandlerMethodArgumentResolver 实现类,感兴趣的能够去看看
在接下来的《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》中讲到 RequestResponseBodyMethodProcessor 既是 HandlerMethodReturnValueHandler 实现类,也是 HandlerMethodArgumentResolver 实现类,用于处理器 @RequestBody 和 @ResponseBody 两个注解
参考文章:芋道源码《精尽 Spring MVC 源码分析》