《学会 SpringMVC 系列 · 参数解析器 ArgumentResolvers》

ops/2024/10/19 3:28:09/

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • ArgumentResolvers
      • 技术简介
      • 内置参数解析器
      • 自定义参数解析器
      • 实战场景
    • 源码知识回顾
    • 总结陈词

CSDN.gif

写在前面的话

前几篇博文,大致了解了SpringMVC请求流程中的参数与返回值的源码分析,后续的几篇博文,会将流程中涉及的若干关键环节单独拿出来讲解,并结合实战中的运用,帮助领略SpringMVC带来的定制和扩展能力。
本篇文章先介绍一下 ArgumentResolvers 相关内容。

相关博文
《《学会 SpringMVC 系列 · 基础篇》
《学会 SpringMVC 系列 · 剖析篇(上)》
《学会 SpringMVC 系列 · 剖析入参处理》
《学会 SpringMVC 系列 · 剖析出参处理》
《学会 SpringMVC 系列 · 返回值处理器》
《学会 SpringMVC 系列 · 消息转换器 MessageConverters》
《学会 SpringMVC 系列 · 写入拦截器 ResponseBodyAdvice》
《学会 SpringMVC 系列 · 剖析初始化》
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》


ArgumentResolvers

技术简介

Spring MVC 的 ArgumentResolvers 参数解析器是负责将请求参数解析为控制器方法参数的关键组件,负责处理请求参数的解析和转换。当一个请求到达控制器时,Spring MVC 使用一系列的ArgumentResolvers来解析传入的参数,根据请求参数的名称和类型,将它们转换为控制器方法可以接受的对象。
Spring MVC 同时提供了 HandlerMethodArgumentResolver 接口,它定义了如何解析方法参数。Spring MVC在处理请求时,会遍历所有的HandlerMethodArgumentResolver实现,尝试解析方法参数。通过该接口可以自定义参数解析器。通过实现这个接口,你可以为控制器方法的参数提供自定义的解析逻辑。这在处理复杂对象、请求体、请求参数等场景中非常有用。


内置参数解析器

Spring MVC 默认提供了多种参数解析器,例如:

  • RequestResponseBodyMethodProcessor: 处理 @RequestBody 和 @ResponseBody 注解,用于解析请求体的 JSON 数据。
  • PathVariableMethodArgumentResolver: 处理 @PathVariable 注解,用于解析 URL 路径中的变量。
  • RequestParamMethodArgumentResolver: 处理 @RequestParam 注解,用于解析请求参数。
  • RequestHeaderMethodArgumentResolver: 处理 @RequestHeader 注解,用于解请求头信息。
  • HttpEntityMethodProcessor: 处理 HttpEntity 类型参数,用于接收 HTTP 请求实体。

下面是若干示例:

java">@GetMapping("/example")
public String example(@RequestParam("name") String name) {return "Hello, " + name;
}@GetMapping("/users/{id}")
public String getUserById(@PathVariable("id") Long userId) {return "User ID is: " + userId;
}@PostMapping("/users")
public String createUser(@RequestBody User user) {// 保存用户信息return "User created successfully";
}@GetMapping("/example")
public String example(@RequestHeader("X-Custom-Header") String headerValue) {return "Header value: " + headerValue;
}

自定义参数解析器

当内置的参数解析器无法满足你的需求时,例如解析复杂的自定义对象或者从非标准的地方获取数据。
你可以通过实现HandlerMethodArgumentResolver接口来创建自定义参数解析器。
该接口定义了两个方法:
supportsParameter:判断当前解析器是否支持解析指定参数
resolveArgument:解析参数并返回解析结果

【具体步骤】
Step1、自定义一个 MyHandlerMethodArgumentResolver,实现 HandlerMethodArgumentResolver 接口。

java">@Slf4j
public class MyHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {/*** 用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument*/@Overridepublic boolean supportsParameter(MethodParameter parameter) {return Student.class.isAssignableFrom(parameter.getParameterType());}/*** 真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象* 用途:仅仅用于测试,解析请求体内容,比如name#张三,age#20,将内容解析组装成Student对象再返回*/@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {String str = getRequestBody(webRequest);String[] split = str.split(",");String name = split[0].split("#")[1];String age = split[1].split("#")[1];return Student.builder().name(name).age(Integer.parseInt(age)).id(1).build();}/*** 从请求体获取内容* 也可以参考RequestResponseBodyMethodProcessor的读取方式*/private String getRequestBody(NativeWebRequest webRequest) throws IOException {HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);assert request != null;BufferedReader reader = request.getReader();StringBuilder sb = new StringBuilder();char[] buf = new char[1024];int rd;while ((rd = reader.read(buf)) != -1) {sb.append(buf, 0, rd);}return sb.toString();}
}

Step2、再SpringMVC的配置类中,添加该入参解析器:

java">@Slf4j
public class CustomConfig implements WebMvcConfigurer {/*** 添加入参处理器*/@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(new MyHandlerMethodArgumentResolver());}
}

Step3、编写测试类

java">/*** 测试自定义入参解析器*/
@ResponseBody
@RequestMapping("/studyJsonCustom")
public Student studyJsonCustom(Student student) {student.setEmail("战神");return student;
}

Step4、启动项目,验证一下效果
image.png


实战场景

上述示例只是为了帮助理解,真实开发中,更多自定义入参解析器的情况是:添加自定义注解,并为其指定特定功能,例如添加可以同时兼容form 和 json 的场景、或支持复杂数据自动解析等等。
自定义参数解析器的应用场景包含但不限于:

  • 复杂对象的解析:当请求参数较多且需要封装成对象时,使用自定义解析器可以简化控制器代码。
  • 请求头解析:可以根据请求头中的信息创建对象。
  • 安全性:可以在解析参数时进行一些安全检查,比如验证用户身份。
  • 数据转换:可以在解析参数时进行数据格式转换,比如将字符串转换为日期对象。

注意事项:

  • 确保supportsParameter方法能够准确地识别需要解析的参数。
  • 在resolveArgument方法中实现具体的解析逻辑,注意异常处理。
  • 考虑解析器的执行顺序,因为Spring MVC会按照注册顺序尝试解析参数。

值得一提的是,解析器可以注册这个,但最终只会生效一个,如果都找不到符合的,都会有兜底的方案,要适当注意一下添加的顺序。


源码知识回顾

本篇为 SpringMVC 源码分析系列文章,总结回顾一下全流程。

【一次请求的主链路节点】
DispatcherServlet#doDispatch(入口方法)
DispatcherServlet#getHandler(根据path找到对应的HandlerExecutionChain
DispatcherServlet#getHandlerAdapter(根据handle找到对应的HandlerAdapter
HandlerExecutionChain#applyPreHandle(触发拦截器的前置逻辑)
AbstractHandlerMethodAdapter#handle(核心逻辑)
HandlerExecutionChain#applyPostHandle(触发拦截器的后置逻辑)

【核心handle方法的主链路节点】
RequestMappingHandlerAdapter#handleInternal(入口方法)
RequestMappingHandlerAdapter#invokeHandlerMethod(入口方法2)
ServletInvocableHandlerMethod#invokeAndHandle(入口方法3)
InvocableHandlerMethod#invokeForRequest(参数和实际执行的所在,3.1)
InvocableHandlerMethod#getMethodArgumentValues(参数处理,3.1.1)
InvocableHandlerMethod#doInvoke(实际执行,3.1.2)
HandlerMethodReturnValueHandlerComposite#handleReturnValue(返回处理,3.2)
image.png

【针对 @RequestBody 和 @ResponseBody 场景】
image.png


总结陈词

本篇博文继请求链路源码分析后,继续介绍了参数解析器ArgumentResolvers的用法,除了熟悉内置参数解析器的原理外,通过实现 HandlerMethodArgumentResolver 接口,你可以灵活地处理控制器方法的参数解析,满足复杂业务需求。在实际开发中,合理使用自定义参数解析器可以提高代码的可读性和可维护性。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

CSDN_END.gif


http://www.ppmy.cn/ops/90115.html

相关文章

前端八股速通(持续更新中...)

1、深拷贝和浅拷贝的区别 浅拷贝&#xff1a;浅拷贝是拷贝一层&#xff0c;引用类型共享地址。 如果属性是基本类型&#xff0c;拷贝的就是基本类型的值。 如果属性是引用类型&#xff0c;拷贝的就是内存地址。 意思是&#xff0c;当进行浅拷贝时&#xff0c;对于对象的每一…

(19)SSM-MyBatis

环境配置 第一步创建一个web工程 导入mybatis包、lombok包、jdbc包 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.16</version></dependency><!-- https://mvnrepo…

C#初级——字典Dictionary

字典 字典是C#中的一种集合&#xff0c;它存储键值对&#xff0c;并且每个键与一个值相关联。 创建字典 Dictionary<键的类型, 值的类型> 字典名字 new Dictionary<键的类型, 值的类型>(); Dictionary<int, string> dicStudent new Dictionary<int, str…

【java基础】徒手写Hello, World!程序

文章目录 前提&#xff1a;java环境变量配置使用vscode编写helloworld解析 前提&#xff1a;java环境变量配置 https://blog.csdn.net/xzzteach/article/details/140869188 使用vscode编写helloworld code .为什么用code看下图 报错了&#xff01;&#xff01;&#xff01;&…

【深度学习】【语音】TTS,Phoneme-Level BERT (PL-BERT),抛弃词级别或超语素级别的预训练模型!

https://github.com/yl4579/PL-BERT 这篇文章的技术重点是提出了一种名为**Phoneme-Level BERT (PL-BERT)**的新模型,用于增强文本到语音(Text-to-Speech,TTS)合成中的韵律,通过语素预测来提高合成语音的自然度。 技术亮点及优势 语素级别的BERT模型: 现有的TTS模型通常…

java上下文切换

上下文切换&#xff08;Context Switch&#xff09;是指操作系统在CPU上切换不同的进程&#xff08;或线程&#xff09;的执行的过程。这通常发生在多任务操作系统中&#xff0c;允许多个进程或线程共享CPU资源。上下文切换确保了操作系统的响应性和多任务处理能力。 具体来说…

PostgreSQL 高阶函数详解:全面深入的功能与实用示例

PostgreSQL 高阶函数详解 PostgreSQL 是一款功能强大的开源关系数据库管理系统&#xff0c;以其丰富的功能和高扩展性著称。在数据处理和分析方面&#xff0c;PostgreSQL 提供了一系列高阶函数&#xff0c;可以极大地简化和优化各种复杂操作。本文将详细介绍 PostgreSQL 的高阶…

后端生成excel文件,VUE请求如何下载excel文件

1、后端提供下载文件接口&#xff0c;例如 GetMapping({"/excelDownload"})public void myExcelDownload(HttpServletResponse response) throws IOException {// 准备表头数据&#xff08;中文名&#xff09;List<String> header generateHeader();// 设置响…