精尽Spring MVC源码分析 - HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读
Spring 版本:5.1.14.RELEASE
该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》
HandlerAdapter 组件
HandlerAdapter 组件,处理器的适配器。因为处理器 handler
的类型是 Object 类型,需要有一个调用者来实现 handler
是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping
注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器
由于 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:
- 《HandlerAdapter 组件(一)之 HandlerAdapter》
- 《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》
- 《HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver》
- 《HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler》
- 《HandlerAdapter 组件(五)之 HttpMessageConverter》
HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver
本文是接着《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》一文来分享 HandlerMethodArgumentResolver 组件。在 HandlerAdapter
执行处理器的过程中,具体的执行过程交由 ServletInvocableHandlerMethod
对象来完成,其中需要先通过 HandlerMethodArgumentResolver 参数解析器从请求中解析出方法的入参,然后再通过反射机制调用对应的方法。
回顾
先来回顾一下 ServletInvocableHandlerMethod
在哪里调用参数解析器的,可以回到 《HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod》 中 InvocableHandlerMethod 小节下面的 getMethodArgumentValues
方法,如下:
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 组合对象,包含了许多的参数解析器
HandlerMethodArgumentResolver 接口
org.springframework.web.method.support.HandlerMethodArgumentResolver
,方法参数解析器
public interface HandlerMethodArgumentResolver {/** * 是否支持解析该参数 */boolean supportsParameter(MethodParameter parameter);/** * 解析该参数 */@NullableObject resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}
类图
因为请求入参的场景非常多,所以 HandlerMethodArgumentResolver 的实现类也非常多,上面仅列出了部分实现类,本文也仅分析上面图中右侧常见的几种参数场景
HandlerMethodArgumentResolverComposite
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
getArgumentResolver(MethodParameter parameter)
方法,获得方法参数对应的 HandlerMethodArgumentResolver 对象,方法如下:
@Nullableprivate 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
实现 supportsParameter(MethodParameter parameter)
方法,如果能获得到对应的 HandlerMethodArgumentResolver 参数处理器,则说明支持处理该参数,方法如下:
@Overridepublic boolean supportsParameter(MethodParameter parameter) { return getArgumentResolver(parameter) != null;}
resolveArgument
实现 resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
方法,解析出指定参数的值,方法如下:
@Override@Nullablepublic 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
执行解析
AbstractNamedValueMethodArgumentResolver
org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver
,实现 ValueMethodArgumentResolver 接口,基于名字获取值的HandlerMethodArgumentResolver 抽象基类。例如说,@RequestParam(value = "username")
注解的参数,就是从请求中获得 username
对应的参数值。