当前位置: 首页 > news >正文

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

📢 大家好,我是 【战神刘玉栋】,有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 请求实体。

下面是若干示例:

@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 接口。

@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的配置类中,添加该入参解析器:

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

Step3、编写测试类

/*** 测试自定义入参解析器*/
@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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java学习笔记(二十):反射、动态代理、日志、类加载器、xml、单元测试Junit、注解
  • EasyX自学笔记3(割草游戏1)
  • Linux字符设备驱动开发
  • SpringBoot3无法注入RocketMQTemplate Bean
  • TabLayout使用以及自定义tab标签
  • MySQL和Redis的数据一致性
  • UE C++ FUdpSender和FUdpReveiver
  • 需要全面学习LangChain?您看这篇就够了
  • .net SqlSugarHelper
  • C# 判断电脑是否联网
  • 保研考研机试攻略:第二章——入门经典(1)
  • Android笔试面试题AI答之Kotlin(4)
  • 高级java每日一道面试题-2024年8月07日-网络篇-你对TCP的三次握手了解多少?
  • OOP经典设计模式
  • Docker 入门全攻略:安装、操作与常用命令指南
  • 时间复杂度分析经典问题——最大子序列和
  • 08.Android之View事件问题
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java多线程(4):使用线程池执行定时任务
  • JS数组方法汇总
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • python 装饰器(一)
  • Redis字符串类型内部编码剖析
  • Theano - 导数
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 深度学习在携程攻略社区的应用
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 问题之ssh中Host key verification failed的解决
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • # Java NIO(一)FileChannel
  • ## 基础知识
  • #QT(一种朴素的计算器实现方法)
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (160)时序收敛--->(10)时序收敛十
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (二)学习JVM —— 垃圾回收机制
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (六)c52学习之旅-独立按键
  • (七)Flink Watermark
  • (四)js前端开发中设计模式之工厂方法模式
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (详细文档!)javaswing图书管理系统+mysql数据库
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)IOS中获取各种文件的目录路径的方法
  • (转)程序员疫苗:代码注入
  • .describe() python_Python-Win32com-Excel
  • .NET C# 配置 Options
  • .net core 6 集成和使用 mongodb
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查