广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >深入理解SpringMVC参数解析器
  • 225
分享到

深入理解SpringMVC参数解析器

2024-04-02 19:04:59 225人浏览 独家记忆
摘要

这篇文章主要讲解了“深入理解springMVC参数解析器”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“深入理解springmvc参数解析器”吧!1.参数解析

这篇文章主要讲解了“深入理解springMVC参数解析器”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“深入理解springmvc参数解析器”吧!

1.参数解析器

HandlerMethodArgumentResolver  就是我们口口声声说的参数解析器,它的实现类还是蛮多的,因为每一种类型的参数都对应了一个参数解析器:

深入理解SpringMVC参数解析器

为了理解方便,我们可以将这些参数解析器分为四大类:

  • xxxMethodArgumentResolver:这就是一个普通的参数解析器。

  • xxxMethodProcessor:不仅可以当作参数解析器,还可以处理对应类型的返回值。

  • xxxAdapter:这种不做参数解析,仅仅用来作为 WEBArgumentResolver 类型的参数解析器的适配器。

  • HandlerMethodArgumentResolverComposite:这个看名字就知道是一个组合解析器,它是一个代理,具体代理其他干活的那些参数解析器。

大致上可以分为这四类,其中最重要的当然就是前两种了。

2.参数解析器概览

接下来我们来先来大概看看这些参数解析器分别都是用来干什么的。

MapMethodProcessor

这个用来处理 Map/ModelMap 类型的参数,解析完成后返回 model。

PathVariableMethodArgumentResolver

这个用来处理使用了 @PathVariable 注解并且参数类型不为 Map 的参数,参数类型为 Map 则使用  PathVariableMapMethodArgumentResolver 来处理。

PathVariableMapMethodArgumentResolver

见上。

ErrorsMethodArgumentResolver

这个用来处理 Error 参数,例如我们做参数校验时的 BindingResult。

AbstractNamedValueMethodArgumentResolver

这个用来处理 key/value 类型的参数,如请求头参数、使用了 @PathVariable 注解的参数以及 Cookie 等。

RequestHeaderMethodArgumentResolver

这个用来处理使用了 @RequestHeader 注解,并且参数类型不是 Map 的参数(参数类型是 Map 的使用  RequestHeaderMapMethodArgumentResolver)。

RequestHeaderMapMethodArgumentResolver

见上。

RequestAttributeMethodArgumentResolver

这个用来处理使用了 @RequestAttribute 注解的参数。

RequestParamMethodArgumentResolver

这个功能就比较广了。使用了 @RequestParam 注解的参数、文件上传的类型  MultipartFile、或者一些没有使用任何注解的基本类型(Long、Integer)以及 String 等,都使用该参数解析器处理。需要注意的是,如果  @RequestParam 注解的参数类型是 Map,则该注解必须有 name 值,否则解析将由  RequestParamMapMethodArgumentResolver 完成。

RequestParamMapMethodArgumentResolver

见上。

AbstractCookieValueMethodArgumentResolver

这个是一个父类,处理使用了 @CookieValue 注解的参数。

ServletCookieValueMethodArgumentResolver

这个处理使用了 @CookieValue 注解的参数。

MatrixVariableMethodArgumentResolver

这个处理使用了 @MatrixVariable 注解并且参数类型不是 Map 的参数,如果参数类型是 Map,则使用  MatrixVariableMapMethodArgumentResolver 来处理。

MatrixVariableMapMethodArgumentResolver

见上。

SessionAttributeMethodArgumentResolver

这个用来处理使用了 @SessionAttribute 注解的参数。

ExpressionValueMethodArgumentResolver

这个用来处理使用了 @Value 注解的参数。

ServletResponseMethodArgumentResolver

这个用来处理 ServletResponse、OutputStream 以及 Writer 类型的参数。

ModelMethodProcessor

这个用来处理 Model 类型参数,并返回 model。

ModelAttributeMethodProcessor

这个用来处理使用了 @ModelAttribute 注解的参数。

SessionStatusMethodArgumentResolver

这个用来处理 SessionStatus 类型的参数。

PrincipalMethodArgumentResolver

这个用来处理 Principal 类型参数,这个松哥在前面的文章中和大家介绍过了(SpringBoot 中如何自定义参数解析器?)。

AbstractMessageConverterMethodArgumentResolver

这是一个父类,当使用 HttpMessageConverter 解析 requestbody 类型参数时,相关的处理类都会继承自它。

RequestPartMethodArgumentResolver

这个用来处理使用了 @RequestPart 注解、MultipartFile 以及 Part 类型的参数。

AbstractMessageConverterMethodProcessor

这是一个工具类,不承担参数解析任务。

RequestResponseBodyMethodProcessor

这个用来处理添加了 @RequestBody 注解的参数。

HttpEntityMethodProcessor

这个用来处理 HttpEntity 和 RequestEntity 类型的参数。

ContinuationHandlerMethodArgumentResolver

AbstractWebArgumentResolverAdapter

这种不做参数解析,仅仅用来作为 WebArgumentResolver 类型的参数解析器的适配器。

ServletWebArgumentResolverAdapter

这个给父类提供 request。

UriComponentsBuilderMethodArgumentResolver

这个用来处理 UriComponentsBuilder 类型的参数。

ServletRequestMethodArgumentResolver

这个用来处理  WebRequest、ServletRequest、MultipartRequest、httpsession、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId  类型的参数。

HandlerMethodArgumentResolverComposite

这个看名字就知道是一个组合解析器,它是一个代理,具体代理其他干活的那些参数解析器。

RedirectAttributesMethodArgumentResolver

这个用来处理 RedirectAttributes 类型的参数,RedirectAttributes 松哥在之前的文章中和大家介绍过:SpringMVC  中的参数还能这么传递?涨姿势了!。

好了,各个参数解析器的大致功能就给大家介绍完了,接下来我们选择其中一种,来具体说说它的源码

3.AbstractNamedValueMethodArgumentResolver

AbstractNamedValueMethodArgumentResolver  是一个抽象类,一些键值对类型的参数解析器都是通过继承它实现的,它里边定义了很多这些键值对类型参数解析器的公共操作。

AbstractNamedValueMethodArgumentResolver 中也是应用了很多模版模式,例如它没有实现  supportsParameter 方法,该方法的具体实现在不同的子类中,resolveArgument 方法它倒是实现了,我们一起来看下:

@Override @Nullable 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 + "]");  }  Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);  if (arg == null) {   if (namedValueInfo.defaultValue != null) {    arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);   }   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 {    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());   }   // Check for null value after conversion of incoming argument value   if (arg == null && namedValueInfo.defaultValue == null &&     namedValueInfo.required && !nestedParameter.isOptional()) {    handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);   }  }  handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);  return arg; }
  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 首先根据当前请求获取一个 NamedValueInfo  对象,这个对象中保存了参数的三个属性:参数名、参数是否必须以及参数默认值。具体的获取过程就是先去缓存中拿,缓存中如果有,就直接返回,缓存中如果没有,则调用  createNamedValueInfo 方法去创建,将创建结果缓存起来并返回。createNamedValueInfo  方法是一个模版方法,具体的实现在子类中。

  3. 接下来处理 Optional 类型参数。

  4. resolveEmbeddedValuesAndExpressions 方法是为了处理注解中使用了 SpEL 表达式的情况,例如如下接口:

@GetMapping("/hello2") public void hello2(@RequestParam(value = "${aa.bb}") String name) {     System.out.println("name = " + name); }

参数名使用了表达式,那么 resolveEmbeddedValuesAndExpressions  方法的目的就是解析出表达式的值,如果没用到表达式,那么该方法会将原参数原封不动返回。4. 接下来调用 resolveName  方法解析出参数的具体值,这个方法也是一个模版方法,具体的实现在子类中。5. 如果获取到的参数值为  null,先去看注解中有没有默认值,然后再去看参数值是否是必须的,如果是,则抛异常出来,否则就设置为 null 即可。6. 如果解析出来的参数值为空字符串  "",则也去 resolveEmbeddedValuesAndExpressions 方法中走一遭。7. 最后则是 WebDataBinder  的处理,解决一些全局参数的问题,WebDataBinder 松哥在之前的文章中也有介绍过,传送门:@ControllerAdvice 的三种使用场景。

大致的流程就是这样。

在这个流程中,我们看到主要有如下两个方法是在子类中实现的:

  • createNamedValueInfo

  • resolveName

在加上 supportsParameter 方法,子类中一共有三个方法需要我们重点分析。

那么接下来我们就以 RequestParamMethodArgumentResolver 为例,来看下这三个方法。

4.RequestParamMethodArgumentResolver

4.1 supportsParameter

@Override public boolean supportsParameter(MethodParameter parameter) {  if (parameter.hasParameterAnnotation(RequestParam.class)) {   if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {    RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);    return (requestParam != null && StringUtils.hasText(requestParam.name()));   }   else {    return true;   }  }  else {   if (parameter.hasParameterAnnotation(RequestPart.class)) {    return false;   }   parameter = parameter.nestedIfOptional();   if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {    return true;   }   else if (this.useDefaultResolution) {    return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());   }   else {    return false;   }  } } public static boolean isSimpleProperty(Class<?> type) {  return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType())); } public static boolean isSimpleValueType(Class<?> type) {  return (Void.class != type && void.class != type &&    (ClassUtils.isPrimitiveOrWrapper(type) ||    Enum.class.isAssignableFrom(type) ||    CharSequence.class.isAssignableFrom(type) ||    Number.class.isAssignableFrom(type) ||    Date.class.isAssignableFrom(type) ||    Temporal.class.isAssignableFrom(type) ||    URI.class == type ||    URL.class == type ||    Locale.class == type ||    Class.class == type)); }

从 supportsParameter 方法中可以非常方便的看出支持的参数类型:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 首先参数如果有 @RequestParam 注解的话,则分两种情况:参数类型如果是 Map,则 @RequestParam 注解必须配置 name  属性,否则不支持;如果参数类型不是 Map,则直接返回 true,表示总是支持(想想自己平时使用的时候是不是这样)。

  3. 参数如果含有 @RequestPart 注解,则不支持。

  4. 检查下是不是文件上传请求,如果是,返回 true 表示支持。

  5. 如果前面都没能返回,则使用默认的解决方案,判断是不是简单类型,主要就是 Void、枚举、字符串、数字、日期等等。

  6. 这块代码其实很简单,支持谁不支持谁,一目了然。

4.2 createNamedValueInfo

@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());  } }

获取注解,读取注解中的属性,构造 RequestParamNamedValueInfo 对象返回。

4.3 resolveName

@Override @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {  HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);  if (servletRequest != null) {   Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);   if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {    return mpArg;   }  }  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; }

这个方法思路也比较清晰:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 前面两个 if 主要是为了处理文件上传请求。

  3. 如果不是文件上传请求,则调用 request.getParameterValues 方法取出参数返回即可。

整个过程还是比较 easy 的。小伙伴们可以在此基础之上自行分析 PathVariableMethodArgumentResolver  的原理,也很容易。

感谢各位的阅读,以上就是“深入理解SpringMVC参数解析器”的内容了,经过本文的学习后,相信大家对深入理解SpringMVC参数解析器这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 深入理解SpringMVC参数解析器

本文链接: https://www.lsjlt.com/news/83356.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 深入理解SpringMVC参数解析器
    这篇文章主要讲解了“深入理解SpringMVC参数解析器”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“深入理解SpringMVC参数解析器”吧!1.参数解析...
    99+
    2022-10-19
  • 深入理解SpringMVC中央调度器DispatcherServlet
    目录SpringMVC请求处理过程:SrpingMVC容器和spring IOC容器关系基于maven创建的一个springmvc工程。1.创建maven项目中使用到了自动创建骨架2...
    99+
    2022-11-13
  • SpringMVC解析post请求参数详解
    目录SpringMVC一,概述二、原理:1.创建form表单css代码 2.准备Student类3.创建启动类4,创建数据库,表使用JDBC把得到的数据入库5.创建StudentCo...
    99+
    2022-11-12
  • Spring MVC请求参数的深入解析
    请求参数解析 客户端请求在handlerMapping中找到对应handler后,将会继续执行DispatchServlet的doPatch()方法。 首先是找到handler对应的...
    99+
    2022-11-12
  • SpringBoot与SpringMVC中参数传递的原理解析
    目录一:普通参数与基本注解二:复杂参数一:普通参数与基本注解 HandlerMapping中找到能处理请求的Handler(Controller,method()) 为当前Handl...
    99+
    2022-11-12
  • 深入理解Go语言文档中的flag.StringVar函数解析命令行参数
    在Go语言中,我们有时需要通过命令行传递参数给程序。为了方便用户设置参数,Go语言提供了flag包来解析命令行参数。其中flag.StringVar函数是flag包中最常用的函数之一,它可以帮助开发者快速的定义并解析命令行参数。本文将深入分...
    99+
    2023-11-03
    Go语言 命令行参数 flagStringVar
  • 深入理解springMVC中的Model和Session属性
    目录springMVC的范围spring的@MODELATTRIBUTEspring Model和Request后边的原因Spring的@SESSIONATTRIBUTE控制对话属性...
    99+
    2022-11-12
  • 深入解析Python中函数的参数与作用域
    传递参数 函数传递参数时的一些简要的关键点: 参数的传递是通过自动将对象赋值给本地变量名来实现的。所有的参数实际上都是通过指针进行传递的,作为参数被传递的对象从来不自动拷贝。 在函数内部的参数名的...
    99+
    2022-06-04
    函数 作用 参数
  • GoComparableType原理深入解析
    目录介绍内部实现现实中的陷阱与应用errors.Is(*Type)(nil) ≠ nilContext Value Key指针类型Struct 类型介绍 在 Go reflec...
    99+
    2023-01-06
    Go Comparable Type原理 Go Comparable
  • 深入了解python的函数参数
    目录位置参数默认参数关键字参数多值参数:总结 位置参数 这是一个求等差数列和的函数,使用必需要传入一个参数n,这就是位置参数 def sum(n): sum=0 ...
    99+
    2022-11-12
  • springmvc中controller参数注解的示例分析
    这篇文章给大家分享的是有关springmvc中controller参数注解的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。绪论相信接触过springmvc的同学都知道,在springmvc的控制层中,我们...
    99+
    2023-05-30
    springmvc controller
  • 深入理解Python对Json的解析
    Json简介 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于JavaScript(Standard ECMA-262 3rd Edition - De...
    99+
    2022-06-04
    Python Json
  • 深入理解SQL解析的内涵
    SQL解析:探究其背后的意义,需要具体代码示例引言:SQL(Structured Query Language)是结构化查询语言的缩写,是一种用于管理和操作关系型数据库的标准语言。作为一种强大的数据操作语言,SQL的解析是数据管理和查询的基...
    99+
    2023-12-28
    解析 (Parsing) SQL (Structured Query Language) 背后的意义 (underlyi
  • 关于SpringMVC在Controller层方法的参数解析详解
    目录自定义参数解析器实现效果实现和配置Spring提供解析器一些使用Tricky总结使用版本: spring-boot: 2.1.6.RELEASE sping: 5.1.8.RE...
    99+
    2022-11-12
  • 深入理解java中反射机制(含数组参数)
    java的反射是我一直非常喜欢的地方,因为有了这个,可以让程序的灵活性大大的增加,同时通用性也提高了很多。反射原理什么的,我就不想做过大介绍了,网上一搜,就一大把。(下面我是只附录介绍下)Reflection 是Java被视为动态(或准动态...
    99+
    2021-12-09
    java入门 java 反射机制
  • Vue路由传参及props解耦深入分析
    目录一、路由传参query传参query配合声明式导航query配合编程式导航query传参的特点params传参路径使用参数占位时路径不使用占位时二、props解耦1.props布...
    99+
    2022-11-13
  • 深入解析SpringBatch适配器
    目录一、SpringBatch适配器二、SpringBatch适配器实战(Tasklet举例)一、SpringBatch适配器 1、SpringBatch分别有读(reader)、处...
    99+
    2022-11-12
  • C++深入浅出讲解缺省参数
    目录缺省参数定义用法缺省参数 一般情况下,函数调用时的实参个数应与形参相同,但为了更方便地使用函数,C++也允许定义具有缺省参数的函数,这种函数调用时,实参个数可以与形参不相同。 定...
    99+
    2022-11-13
  • 基于springmvc之常用注解,操作传入参数
    目录springmvc常用注解,操作传入参数@RequestParam@RequestBody@PathVariable@RequestHeader@CookieValue@Mode...
    99+
    2022-11-12
  • Flutter渲染原理深入解析
    目录Widget Element RenderObject之间的关系1 Widget2 Element3 RenderObject4 结合图说一下其三者的关系5 一些小问题Widge...
    99+
    2023-05-15
    Flutter渲染原理 Flutter渲染
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作