该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读

Spring 版本:5.2.4.RELEASE

该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》

HandlerAdapter 组件

HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致 Spring MVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器

由于 HandlerMapping 组件涉及到的内容较多,考虑到内容的排版,所以将这部分内容拆分成了五个模块,依次进行分析:

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);
/**
* 解析该参数
*/
@Nullable
Object 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 对象,方法如下:

@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

实现 supportsParameter(MethodParameter parameter) 方法,如果能获得到对应的 HandlerMethodArgumentResolver 参数处理器,则说明支持处理该参数,方法如下:

@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}

resolveArgument

实现 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 执行解析

AbstractNamedValueMethodArgumentResolver

org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver,实现 ValueMethodArgumentResolver 接口,基于名字获取值的HandlerMethodArgumentResolver 抽象基类。例如说,@RequestParam(value = "username") 注解的参数,就是从请求中获得 username 对应的参数值。

精尽Spring MVC源码分析 - HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver的更多相关文章

  1. 精尽Spring MVC源码分析 - HandlerAdapter 组件(一)之 HandlerAdapter

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  2. 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  3. 精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  4. 精尽Spring MVC源码分析 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. 精尽Spring MVC源码分析 - MultipartResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  6. 精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  7. 精尽Spring MVC源码分析 - HandlerMapping 组件(三)之 AbstractHandlerMethodMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  8. 精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  9. 精尽Spring MVC源码分析 - HandlerExceptionResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

随机推荐

  1. 面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?

    概念 内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用.即无用对象持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间浪费. 可达 ...

  2. 02python开发之基本运算符

    02 python开发之基本运算符 目录 02 python开发之基本运算符 2 基本运算符 2.1 算数运算符 2.1.1 种类 2.1.2 用法 2.2 比较运算符 2.2.1 种类 2.2.2 ...

  3. css3系列之过渡transition

    transition 设置变换属性的过渡效果,举个例子,也就是说, 如果从width100px 变成 width200px,要以什么方式进行变换,速度,时间,类型等. transition trans ...

  4. css3系列之详解box-shadow

    box-shadow box-shadow呢 是设置元素的阴影效果的,利用这个属性,可以设计很多很炫丽的效果,不信? 等下,学完,我们就来完成下面这两个效果 首先 先了解一下,box-shadow 的 ...

  5. 记一次Ddos遭遇

    万年不用的vps最近借朋友用了几天,今天突然跟我说连不上了 上服务器先暴力重启一波 还是不行,netstat一看 端口的连接状态是这个样子: 估计连接被打满了,遂换了个端口 重启之 问题解决

  6. 微服务手册:API接口9个生命节点,构建全生命周期管理

    互联网应用架构:专注编程教学,架构,JAVA,Python,微服务,机器学习等领域,欢迎关注,一起学习. 对于API,在日常的工作中是接触最多的东西,特别是我们软件这一行,基本就是家常便饭了,在百度百 ...

  7. [自学] MIT的EECS本科+研究生课程【持续更新中-2020.06.02】

    前言 我的本科是读的电子信息工程,研究生跟着老师做项目,参与到深度学习中来,毕业后做了算法工程师,工作之后愈发发现,不论从事什么岗位,基础都很重要,但现在也没有时间再读一遍本科了,自学的话也不知道从何 ...

  8. 真香!Python开发工程师都选择这个数据库:因为它免费

    数据库类别 既然我们要使用关系数据库,就必须选择一个关系数据库. 目前广泛使用的关系数据库也就这么几种: 付费的商用数据库: Oracle,典型的高富帅: SQL Server,微软自家产品,Wind ...

  9. WPF 学习笔记(一)

    一.概述 WPF(Windows Presentation Foundation)是微软推出的基于Windows 的用户界面框架,随着.NET Framework 3.0发布第一个版本.它提供了统一的 ...

  10. Python正则表达式re模块和os模块实现文件搜索模式匹配

    ☞ ░ 前往老猿Python博文目录 ░ 因测试需要,需要提供一个可以指定目录搜索符合条件的文件名函数,搜索时可以通过*(星号)匹配0-n个字符,?(问号)匹配任意1个字符,可以指定多个文件类型,每个 ...