在一个 Web 请求中,参数我们无非就是放在地址栏或者请求体中,个别请求可能放在请求头中。

放在地址栏中,我们可以通过如下方式获取参数:

String javaboy = request.getParameter("name ");
放在请求体中,如果是 key/value 形式,我们可以通过如下方式获取参数:

String javaboy = request.getParameter("name ");
如果是 JSON 形式,我们则通过如果如下方式获取到输入流,然后解析成 JSON 字符串,再通过 JSON 工具转为对象:

BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));String json = reader.readLine();reader.close();User user = new ObjectMapper().readValue(json, User.class);
如果参数放在请求头中,我们可以通过如下方式获取:

String javaboy = request.getHeader(“name”);
如果你用的是 Jsp/Servlet 那一套技术栈,那么参数获取无外乎这几种方式。

如果用了 SpringMVC 框架,有的小伙伴们可能会觉得参数获取方式太丰富了,各种注解如 @RequestParam、@RequestBody、@RequestHeader、@PathVariable,参数可以是 key/value 形式,也可以是 JSON 形式,非常丰富!但是,无论多么丰富,最底层获取参数的方式无外乎上面几种。

那有小伙伴要问了,SpringMVC 到底是怎么样从 request 中把参数提取出来直接给我们用的呢?例如下面这个接口:

@RestControllerpublic class HelloController { @GetMapping(“/hello”) public String hello(String name) { return "hello "+name; }}
我们都知道 name 参数是从 HttpServletRequest 中提取出来的,到底是怎么提取出来的?这就是松哥今天要和大家分享的话题。

1.自定义参数解析器
为了搞清楚这个问题,我们先来自定义一个参数解析器看看。

自定义参数解析器需要实现 HandlerMethodArgumentResolver 接口,我们先来看看该接口:

public interface HandlerMethodArgumentResolver { boolean supportsParameter(MethodParameter parameter); @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}
这个接口中就两个方法:

supportsParameter:该方法表示是否启用这个参数解析器,返回 true 表示启用,返回 false 表示不启用。
resolveArgument:这是具体的解析过程,就是从 request 中取出参数的过程,方法的返回值就对应了接口中参数的值。
自定义参数解析器只需要实现该接口即可。

假设我现在有这样一个需求(实际上在 Spring Security 中获取当前登录用户名非常方便,这里只是为了该案例而做,勿抬杠):

假设我现在系统安全框架使用了 Spring Security如果我在接口的参数上添加了 @CurrentUserName 注解,那么该参数的值就是当前登录的用户名,像下面这样:

@RestControllerpublic class HelloController { @GetMapping(“/hello”) public String hello(@CurrentUserName String name) { return "hello "+name; }}
要实现这个功能,非常 easy,首先我们自定义一个 @CurrentUserName 注解,如下:

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public @interface CurrentUserName {}
这个注解没啥好解释的。

接下来我们自定义参数解析器 CurrentUserNameHandlerMethodArgumentResolver,如下:

public class CurrentUserNameHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().isAssignableFrom(String.class)&¶meter.hasParameterAnnotation(CurrentUserName.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); return user.getUsername(); }}
supportsParameter:如果参数类型是 String,并且参数上有 @CurrentUserName 注解,则使用该参数解析器。
resolveArgument:该方法的返回值就是参数的具体值,当前登录用户名从 SecurityContextHolder 中获取即可。
最后,我们再将自定义的参数解析器配置到 HandlerAdapter 中,配置方式如下:

@Configurationpublic class WebConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List resolvers) { resolvers.add(new CurrentUserNameHandlerMethodArgumentResolver()); }}
至此,就算配置完成了。

接下来启动项目,用户登录成功后,访问 /hello 接口,就可以看到返回当前登录用户数据了。

这就是我们自定义的一个参数类型解析器。可以看到,非常 Easy。

在 SpringMVC 中,默认也有很多 HandlerMethodArgumentResolver 的实现类,他们处理的问题也都类似,松哥再给大家举个例子。

2.PrincipalMethodArgumentResolver
如果我们在项目中使用了 Spring Security,我们可以通过如下方式获取当前登录用户信息:

@GetMapping(“/hello2”)public String hello2(Principal principal) { return "hello " + principal.getName();}
即直接在当前接口的参数中添加 Principal 类型的参数即可,该参数描述了当前登录用户信息,这个用过 Spring Security 的小伙伴应该都知道。

那么这个功能是怎么实现的呢?当然就是 PrincipalMethodArgumentResolver 在起作用了!

我们一起来看下这个参数解析器:

public class PrincipalMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return Principal.class.isAssignableFrom(parameter.getParameterType()); } @Override public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); if (request == null) { throw new IllegalStateException("Current request is not of type HttpServletRequest: " + webRequest); } Principal principal = request.getUserPrincipal(); if (principal != null && !parameter.getParameterType().isInstance(principal)) { throw new IllegalStateException(“Current user principal is not of type [” + parameter.getParameterType().getName() + "]: " + principal); } return principal; }}
supportsParameter:这个方法主要是判断参数类型是不是 Principal,如果参数类型是 Principal,就支持。
resolveArgument:这个方法的逻辑很简单,首先获取原生的请求,再从请求中获取 Principal 对象返回即可。
是不是很简单,有了这个,我们就可以随时加载到当前登录用户信息了。

3.RequestParamMapMethodArgumentResolver
松哥再给大家举个例子:

@RestControllerpublic class HelloController { @PostMapping(“/hello”) public void hello(@RequestParam MultiValueMap map) throws IOException { //省略… }}
这个接口很多小伙伴可能都写过,使用 Map 去接收前端传来的参数,那么这里用到的参数解析器就是 RequestParamMapMethodArgumentResolver。在此我向大家推荐一个架构学习交流圈。交流学习指导伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

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); if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) { // MultiValueMap Class<?> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve(); if (valueType == MultipartFile.class) { MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest); return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0)); } else if (valueType == Part.class) { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) { Collection parts = servletRequest.getParts(); LinkedMultiValueMap

resolveArgument:具体解析分为两种情况:MultiValueMap 和其他 Map,前者中又分三种情况:MultipartFile、Part 或者其他普通请求,前两者可以处理文件上传,第三个就是普通参数。如果是普通 Map,则直接获取到原始请求参数放到一个 Map 集合中返回即可。

手把手教你在 SpringBoot 自定义参数解析器相关推荐

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

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

  2. Spring自定义参数解析器

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

  3. springmvc自定义参数解析器

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

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

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

  5. SpringBoot自定义参数验证器

    前要 之前我们介绍了JSR-303验证方式,十分的方便Spring都帮我们封装好了,但是对一些复杂的验证,还是需要更加灵活的验证器的. JSR-303验证器传送门:https://www.jiansh ...

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

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

  7. SpringBoot 2.x yml 文件中自定义参数解析对象

    在SpringBoot中通过自定义参数可以实现很多重要的功能来达到解耦. SpringBoot 自定义参数的类型有: 简单键值对,自定义对象,数组,List,Map,List<Map> 1 ...

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

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

  9. 手把手教你创建springBoot项目

    **# 手把手教你创建springBoot项目 千里之行 始于足下 简介 springboot是用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(properties或yml文 ...

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

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

最新文章

  1. bottle框架学习(八)之Mysql数据库的操作
  2. SQL SERVER大话存储结构:数据库数据文件
  3. 【Zookeeper】编程实战之Zookeeper分布式锁实现秒杀
  4. 消除switch语句以获得更好的代码结构
  5. Java设计模式(1 / 23):策略模式
  6. unix和linux命令_Linux vs. Unix,在命令行中清理数据,为儿童准备的15本书,以及更多必读内容
  7. 【日常水题-bfs】马的遍历
  8. App Tamer Mac版常见问题解答
  9. DOORS入门教程系列:基本概念介绍(一)
  10. proteus仿真arduino控制舵机
  11. 第9章 数据库完整性
  12. 使用百度地图API来完成交大校园巴士时刻表
  13. 小新pro13睡眠后无法唤醒_东芝笔记本睡眠后无法唤醒怎么办
  14. 2020Android手机专业摄影,这 7 款手机,就是 2020 上半年的拍照巅峰
  15. android 获取 meid 代码,Android 各个版本获取IMEI、MEID
  16. GB2312汉字区位码、交换码和机内码
  17. Iptables封禁IP,记录地址
  18. AES128加密算法与实现1
  19. SPSS Modeler 数据定义(指南 第二章)
  20. 使用chatgpt实现微信自动回复

热门文章

  1. 计算机四级网络考试容易蒙吗,计算机四级网络工程师通过率有多少
  2. java 订单编号_生成订单编号 用java怎么处理
  3. 计算机一级查询证书编号,如何查询计算机一级证书编号
  4. GD32移植FATFS文件系统
  5. macbook更新系统服务器,苹果电脑如何升级系统_苹果电脑怎么更新版本-win7之家...
  6. Gradle全版本资源下载
  7. Prometheus监控系统+Grafana可视化工具搭建
  8. 声音鉴定-趣味测试-源码
  9. Android APP开发入门教程-Button
  10. vue 电子签名插件