本文主要是通过自定义参数解析器学习springmvc的扩展点
主要想完成的操作是:
通过自定义一个注解,将入参中的 json格式的字符串,通过自定义的参数解析器转换为指定类型的list数组

应用

自定义参数解析器

public class MyHandlerMethodArgumentResolverTest implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {Json ann = (Json) methodParameter.getParameterAnnotation(Json.class);System.out.println(methodParameter);return ann != null;}@Overridepublic Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {Json ann = (Json) methodParameter.getParameterAnnotation(Json.class);String param1 = ann.value();System.out.println(param1);System.out.println(ann.array());String parameter = nativeWebRequest.getParameter(param1);return JSON.parseArray(parameter, ann.array());}
}

将自定义参数解析器加入到spring容器中

@Configuration
@ComponentScan("com.springmvc")
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(new MyHandlerMethodArgumentResolverTest());}// @Override// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {//     converters.add(new MappingJackson2HttpMessageConverter());//     converters.add(new StringHttpMessageConverter());// }
}

自定义注解Json

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Json {String value() default "";Class<?> array();
}

controller接口


@RequestMapping("/testMyAnno")
public String testMyAnno(@Json(value = "testList", array = ImageInfo.class) List<ImageInfo> testList,@RequestParam("haha") String userName) {System.out.println("list信息是:" + testList);System.out.println("userName是:" + userName);UserInfo userInfo = new UserInfo(userName, testList);System.out.println(userInfo.toString());return "success";
}

请求url:

http://127.0.0.1:8080/testMyAnno.do?testList=[{"imageType":3,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":4,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":6,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":1,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"},{"imageType":2,"imageUrl":"http://f.souche.com/40accdeff31be8bf84fbb69e632e37d9.png"}]&haha=yesdy

需要解释一下,我这里想要实现的效果就是:
对于加了@Json注解的参数,直接将其String字符串解析为@Json注解中指定的Class类型的数组

源码

将自定义参数解析器加入到spring中

首先来说如何将一个自定义的参数解析器交给spring
对于springmvc项目,spring会初始化requestMappingHandlerAdapter对象,就是在下面这个类中,通过@Bean初始化的

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {}

在这个方法中,有一行代码,和我们将自定义参数解析器加入到容器中有关系,就是

adapter.setCustomArgumentResolvers(this.getArgumentResolvers());

这里会调用getArgumentResolvers获取到所有的参数解析器,在这个方法的处理逻辑中,会调用所有WebMvconfigurer的addArgumentResolvers()方法,所以,我们只需要在继承了WebMvcConfigurer的实现类中,覆写addArgumentResolvers(),然后将自定义的参数解析器,添加到resolvers 中即可

WebMvcConfigurationSupport这个类是在我们添加@EnableWebMvc注解的时候,是会通过@Import注入一个继承了该类的全配置类

这个方法就不做过多的解释了,debug看下源码,就一目了然了,没有什么复杂的逻辑

如何使用自定义参数解析器

我们自定义了参数解析器,并且放入到了容器中,那所有的请求,都会执行到org.springframework.web.servlet.DispatcherServlet#doDispatch,在这个方法中,解析到当前方法所使用的handlerMapping和handlerAdapter之后,就会开始调用目标方法
在调用目标方法之前,会对入参一一进行解析
我们这篇文章,只关心参数解析的逻辑,所以调用链是这样的

org.springframework.web.servlet.DispatcherServlet#doDispatchorg.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handleorg.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handleInternalorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethodorg.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandleorg.springframework.web.method.support.InvocableHandlerMethod#invokeForRequestorg.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
判断使用哪个参数解析器解析

对参数的解析,核心的就是在这几行代码中,在这个代码块外层是一个for循环,会遍历所有的args

argumentResolvers.supportsParameter(parameter):这行代码中,会拿着所有的参数解析器,去对当前参数args[i],进行判断,看哪个参数解析器可以解析该参数

/*** 遍历所有的参数解析器,看哪个解析器可以解析,如果参数解析器返回true,就表示可以解析* 如果我们要扩展参数解析器,就需要看下这里的逻辑* 需要学习下这里的argumentResolvers是在哪里赋值的*/
if (this.argumentResolvers.supportsParameter(parameter)) {try {/*** 这里就是用parameter对应的解析器去解析该参数*/args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);continue;}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);}throw ex;}
}
/*** 遍历所有的参数处理器,找到处理该parameter的处理器,然后存入到map集合中,* 第二次获取处理该参数的处理器时,就无须再次遍历所有的support方法,直接从map缓存中获取即可* @param parameter* @return*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {if (logger.isTraceEnabled()) {logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +parameter.getGenericParameterType() + "]");}/*** 核心方法就是这里,上面会遍历所有的参数解析器,依次去调用对应的supports方法,判断是否可以处理parameter* 如果可以,就返回true,然后会把对应的methodArgumentResolver和对应的parameter关联起来*/if (methodArgumentResolver.supportsParameter(parameter)) {result = methodArgumentResolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;
}

这个方法就是最为核心的方法之一:判断参数解析器是否可以解析当前参数;
这里会获取到所有的参数解析器,包括我们自定义的,举例:我们自定义的参数解析器A支持解析args[i]之后,那就会把A放入到argumentResolverCache这个缓存中
这里的思想就是:我们只需要提供参数解析器,并且指定我们自定义的参数解析器要解析哪种参数,spring自己会获取到所有的参数解析器,去和要解析的参数进行匹配,这种设计模式应该是模板设计模式,总之思想就是这样的

使用对应的参数解析器解析参数

下一个比较核心的方法就是去解析的逻辑

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("Unknown parameter type [" + parameter.getParameterType().getName() + "]");}/*** 这里调用的就是处理的方法*/return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

可以看到,spring会从map集合中根据parameter,获取这个参数所使用的参数解析器,然后去解析
这里的resolveArgument就会去调用到自定义参数解析器中的解析方法

总之:思想就是这样的
1、spring提供了参数解析器的接口,如果我们要自定义,就实现该接口
2、然后把自定义的参数解析器放到spring容器中
3、在方法被调用的时候,spring会拿着spring自己的 + 我们自定义的参数解析器去遍历
4、判断到参数可以被哪个解析器解析之后,就会把该参数交给对应的参数解析器去解析

springmvc应用-自定义参数解析器相关推荐

  1. springmvc自定义参数解析器

    由于开发中一般使用参数提交方式是json格式,对于单个参数的传递使用无法接收只能自定义参数解析器处理 springmvc的自定义参数解析器实现HandlerMethodArgumentResolver ...

  2. springmvc自定义参数解析器/类型转换器

    概述 有些时候我们需要对GET请求的入参做自定义的处理,比较常见的就是字符串反序列化时间类型了,常用的像@DateTimeFormat注解,但是这需要在每个入参的属性上都加上这个注解,比较费手,那么我 ...

  3. springMvc(实现HandlerMethodArgumentResolver)自定义参数解析器

    由于之前用@RequestParam无法接收request payload 正文格式为json格式的字符串,只能使用@RequestBody整个接收,觉得麻烦,但是spring自带的参数解析器不具有这 ...

  4. Spring自定义参数解析器

      虽然Spring提供了比较完善的参数解析器,但是对于一些特殊的数据类型我们还是需要进行特殊处理,这样会提高代码的复杂度,增加冗余的代码,降低代码可读性和可维护性.所以自定义参数解析器是一个很好的解 ...

  5. Spring MVC自定义类型转换器Converter、参数解析器HandlerMethodArgumentResolver

    文章目录 一.前言 二.类型转换器Converter 1.自定义类型转换器 三.参数解析器 1.自定义分页参数解析器 2.自定义注解参数解析器 一.前言 Spring MVC源码分析相关文章已出: S ...

  6. spring MVC使用自定义的参数解析器解析参数

    目录 写在前面 编写自定义的参数解析器解析请求参数 项目结构 定义注解 实体类 controller 定义参数解析器 注册参数解析器 启动项目 发起请求查看结果 写在前面 如果还有小伙伴不知道spri ...

  7. SpringMVC 参数解析器

    一.问题 springMVC对于下面这种接口,参数是怎么解析的: @GetMapping("/hello/{id}") public void hello3(@PathVariab ...

  8. SpringBoot--网上商城项目(自定义的参数解析器、购物车后台前台功能、商品详情页)

    目录 一.自定义的参数解析器 关于Mybatis-plus时间字段代码生成问题 报错信息:Caused by: java.lang.IllegalStateException: No typehand ...

  9. 【SpringMVC】SpringMVC模型数据+视图解析器

    目录 一.模型数据-如何将数据存入request域 二.模型数据-如何将数据存入session域 三.@ModelAttribute 四.视图解析器 相关文章 [SpringMVC]入门篇:带你了解S ...

  10. 拦截器HandlerInterceptor+方法参数解析器HandlerMethodArgumentResolver用于统一获取当前登录用户信息

    文章目录 前言 一.拦截器+方法参数解析器 是什么? 二.具体实现步骤 1.自定义权限拦截器LoginInterceptor拦截所有request请求,并将token解析为currentUser,最终 ...

最新文章

  1. 在PHP里使用 ImageMagick 生成 base64 图片
  2. android动画延迟执行,Android 过渡动画框架
  3. DeprecationWarning: the md5 module is deprecated; use hashlib instead import md5的解决办法
  4. 安装ISO系统(原版系统)系统终极方法
  5. 【汇编语言-3】 代码、数据、堆栈在同一个段
  6. Mac 运行goland出现can‘t load package: package xxxx is not in GOROOT问题排查
  7. 快点来学吧!mysql批量插入数据
  8. 3. Node.js REPL(交互式解释器)
  9. 在.NET开发中的单元测试工具之(2)——xUnit.Net
  10. 【codeVS 1082】树状数组(区间修改,区间查询)模版题
  11. 2021《考研数学接力题典1800》勘误表(附学霸高分笔记)
  12. 计算机科技公司的设备签收单,【验收单格式】_设备验收单格式与范本
  13. 微信开放平台的使用介绍,配置流程
  14. linux lilo_LInux装箱商(LILO)| 免费和开源软件
  15. QtQuick串口编程Demo
  16. 什么是虚继承?虚基类?
  17. python tkinter 如何锁定窗体尺寸 防止用户调整尺寸 固定大小
  18. 欧洲词典 ABBYY Lingvo European 1.11.0 特别版 Mac 离线翻译软件
  19. 武汉大学计算机学院卓越工程师班弘毅学堂,武汉大学弘毅学堂培养方案(2018级试行)...
  20. 第一单元:连续时间信号与离散时间信号

热门文章

  1. 机器学习- 吴恩达Andrew Ng Week1 知识总结 Introduciton
  2. 翻译:谁将在AI中赚钱?by Simon Greenman
  3. 易筋SpringBoot 2.1 | 第廿篇:SpringBoot的复杂JPA以及源码解析
  4. 数据集:科研经费投入对生产能力提高的影响
  5. 402.移掉K位数字
  6. Inception v1
  7. jsp语法 --模板元素,表达式,脚本片段,声明,注释
  8. 机器学习概念篇:监督学习、过拟合,正则化,泛化能力等概念以及防止过拟合方法总结
  9. IDEA连接Spark集群执行Scala程序
  10. 每日一题/011/线性代数/高等代数/两个上三角矩阵的乘积还是上三角矩阵