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

Spring MVC 参数解析(13)

目录

简介

调用流程

1. 首先,还是需要进行到前端控制器的doDispatch方法,这是我们的调用Spring MVC的核心入口方法

2. 在doDispatch方法内部,我们调用到了HandlerAdapter.handle(*****) 方法

3. 最终,我们会来到 RequestMappingHandlerAdapter 的 invokeHandlerMethod 方法;在这个方法内部,我们会设置 参数解析器 和 返回值解析器

4.  来到当前方法 invokeHandlerMethod 调用 Controller业务类处,进行业务类的调用;进行 参数解析 与 返回值处理

 5. 进入 ServletInvocableHandlerMethod 的 invokeAndHandle 方法处,进行参数解析 与 返回值处理

参数解析

case1:@RequestParam注解的参数解析器

case 2 :@PathVariable 注解的参数解析器


简介

据我所知:

参数解析器共分26种,针对不同的注解参数,有不同的参数解析器;

返回值解析器共分15种,针对不同的返回值,调用不同的返回值解析器;

其实,参数解析的核心代码在 HandlerMethodArgumentResolverCompositeresolveArgument 方法处,以后直接锁定这个方法就可以快速进行debug操作:

上一篇博客Spring MVC 的调用(12)_chen_yao_kerr的博客-CSDN博客已经对Spring MVC整体调用流程进行了梳理,但是在上一篇中关于HandlerAdapter调用到业务类,最终返回modelAndView的步骤中,我们只是梳理了整体流程, 并没有详细的分析如何进行参数传递的。本篇则是对上一篇的第五、六、七步中进行参数解析的补充.

其实,参数解析就是发生在HandlerAdapter调用到具体的业务类之前进行的,看下图:

调用流程

1. 首先,还是需要进行到前端控制器的doDispatch方法,这是我们的调用Spring MVC的核心入口方法

2. 在doDispatch方法内部,我们调用到了HandlerAdapter.handle(*****) 方法

3. 最终,我们会来到 RequestMappingHandlerAdapter 的 invokeHandlerMethod 方法;在这个方法内部,我们会设置 参数解析器 和 返回值解析器

参数解析器有26种:

返回值解析器:

 源码如下:

@Nullableprotected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {//获取数据绑定工厂  @InitBinder注解支持,没太多用WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);//Model工厂,收集了@ModelAttribute注解的方法ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);//可调用的方法对象ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {//设置参数解析器invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {//设置返回值解析器invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}//设置参数绑定工厂invocableMethod.setDataBinderFactory(binderFactory);//设置参数名称解析类invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);ModelAndViewContainer mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));//调用有@ModelAttribute注解的方法。每次请求都会调用有@ModelAttribute注解的方法//把@ModelAttribute注解的方法的返回值存储到 ModelAndViewContainer对象的map中了modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);//异步处理WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);return "Resume with async result [" + formatted + "]";});invocableMethod = invocableMethod.wrapConcurrentResult(result);}//Controller方法调用,重点看看invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}

4.  来到当前方法 invokeHandlerMethod 调用 Controller业务类处,进行业务类的调用;进行 参数解析返回值处理

 5. 进入 ServletInvocableHandlerMethod invokeAndHandle 方法处,进行参数解析 与 返回值处理

参数解析

参数解析,需要针对不同的注解传递的参数进行解析,不能一概而论;首先来到参数解析的方法中。即 ServletInvocableHandlerMethod  类的 invokeForRequest 方法中:

	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//获取参数数组,重点看Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}return doInvoke(args);}

就是获取到所有的参数进行反射调用,反射调用在分析Spring源码的时候已经说过很多次了,就不再累赘了。下面针对不同的case分析不同的参数解析流程:

case1:@RequestParam注解的参数解析器

测试的业务方法

 @RequestMapping("/queryUser")public String queryUser(@RequestParam String language, HttpSession session) {session.setAttribute("language",language);return "ok";}

 URL: http://localhost:9090/user/queryUser?language=en

 测试结果:

源码分析

1、首先,肯定是来到参数解析的核心方法处

 2. 进入核心方法

    a, 首先,拿到当前方法对应的参数名称、参数类型、参数使用的注解、当前方法属于的哪个类、参数解析器等各种各样信息;

b.  进入参数解析的核心方法

 c. 根据参数获取对应的参数解析器

其实,就是根据步骤 a 获取到的 MethodParameter 对象,直接从缓存中拿到对应的参数解析器而已。拿到的参数解析器是 RequestParamMethodArgumentResolver。

d. 最后,自然是调用这个类进行参数解析了。具体调用的是 resolveArgument 方法进行 参数解析并返回数组了。

其实参数解析很简单,就是获取到参数的包装类,根据包装类获取到当前参数名、根据参数名到Request中获取参数值(此处就是调用 request.getParameterValues(name))

最终返回获取到的实际参数值并且返回。因为每个方法的参数可能是多个,因此我们是按照方法的参数进行遍历操作的,而且每个参数都是有对应下标的,这样返回值就不会乱、也不会少 。

case 2 :@PathVariable 注解的参数解析器

测试业务方法

    @RequestMapping("/queryUser2/{id}")public @ResponseBody String queryUser2(@PathVariable("id") String userId) {return "hello user :" + userId;}

URL:http://localhost:9090/user/queryUser2/1234

测试结果:

 源码分析:

文章开头就说了, 参数解析的核心代码在HandlerMethodArgumentResolverCompositeresolveArgument 方法处,直接把断点打在此处:

获取参数解析器,@PathVariable 对应的参数解析器为 PathVariableMethodArgumentResolver

根据参数解析器进行参数解析:

我们发现,这是相同的代码,进去以后,其实会调用父类的钩子方法,返回到当前参数解析器 PathVariableMethodArgumentResolver中调用钩子方法中

 其实,它就是根据拿 requet.getAttribute(***)获取参数值而已。所谓的Spring MVC,其实就是对 Servlet调用的封装而已。传统的 jsp + servlet调用,我们都是通过 request 直接拿参数的。而Spring mvc 自己封装了 request拿参数的过程而已。

下面列出常用的参数解析器以及对应的注解,我们直接看解析器的 resolveName 方法,就可以知道具体的解析过程。

  1. RequestHeaderMethodArgumentResolver   用来处理使用了 @RequestHeader 注解,并且参数类型不是 Map 的参数
  2. RequestHeaderMapMethodArgumentResolver 很明显,这个用来处理使用 @RequestHeader 注解,并且参数类型是 Map 的参数
  3. PathVariableMethodArgumentResolver  处理使用 @PathVariable 注解并且参数类型不为 Map 的参数
  4. PathVariableMapMethodArgumentResolver  处理使用 @PathVariable 注解并且参数类型为 Map 的参数
  5. RequestAttributeMethodArgumentResolver  处理使用 @RequestAttribute 注解的参数
  6. ServletCookieValueMethodArgumentResolver  处理使用 @CookieValue 的参数
  7. SessionAttributeMethodArgumentResolver  处理使用 @SessionAttribute 注解的参数
  8. RequestParamMethodArgumentResolver  这个功能就比较广了。使用了 @RequestParam 注解的参数、文件上传的类型 MultipartFile、或者一些没有使用任何注解的基本类型(Long、Integer)以及 String 等,都使用该参数解析器处理。变量不能是Map
  9. RequestParamMapMethodArgumentResolver   针对上一个,提供Map支持

至于不常用的参数解析器,按照我说的方法,直接找到以下方法打断点,就可以快速锁定:

相关文章:

Spring MVC 参数解析(13)

目录 简介 调用流程 1. 首先,还是需要进行到前端控制器的doDispatch方法,这是我们的调用Spring MVC的核心入口方法 2. 在doDispatch方法内部,我们调用到了HandlerAdapter.handle(*****) 方法 3. 最终,我们会来到 RequestMappi…...

探索 Qt WebEngineWidgets:从底层原理到高级应用与技巧

探索 Qt WebEngineWidgets:从底层原理到高级应用与技巧 (Exploring Qt WebEngineWidgets: From Fundamentals to Advanced Applications and Techniques 一、Qt WebEngineWidgets 模块简介及原理 (Introduction and Principles of Qt WebEngineWidgets Module)1. Qt…...

leetcode160. 相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 图示两个链表在节点 c1 开始相交: 题目数据 保证 整个链式结构中不存在环。 注意,函数返回结果后&…...

核心业务7:放款实现

核心业务7:放款实现 1.放款实现流程 -------------------未完成生成借款人还款计划和投资人回款计划-------------- 2.数据库表 3.前端流程 4.汇付宝流程 5.尚融宝后端流程 -------------------未完成生成借款人还款计划和投资人回款计划-------------- -------------…...

STM32F4系列芯片RTC模块介绍

RTC是“实时时钟”的缩写,它是一种芯片,在计算机等电子产品中广泛应用。RTC提供了实时时钟计时功能和存储时间的能力,即时钟模块,常用于控制和记录时间的应用场合。 RTC的工作原理 RTC主要由时钟电路、电源管理电路、晶振电路、…...

MySQL 在线人数 场景分析

一般在直播或者游戏中经常会统计用户在线人数,主要分为求每个时刻的在线人数和求某个时刻的在线人数两种。 【场景】:某个时刻的在线人数、每个时刻的在线人数 【知识点】:窗口函数、时间函数、sum(tag) over (order by dt,tag desc rows b…...

使用mybatis和dynamic-datasource-spring-boot-starter动态切换数据源操作数据库

记录:415 场景:使用mybatis和dynamic-datasource-spring-boot-starter动态切换数据源操作数据库。 版本:JDK 1.8,Spring Boot 2.6.3,dynamic-datasource-spring-boot-starter-3.3.2,mybatis-3.5.9。 源码:https://github.com/b…...

【日常刷题】迷宫问题

描述 定义一个二维数组 N*M ,如 5 5 数组下所示: int maze[5][5] { 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走…...

【Python童年游戏】满满的回忆杀—那些年玩过的童年游戏你还记得吗?那个才是你的菜?看到第一个我就泪奔了(致我们逝去的青春)

导语 滴一一学生卡🙌 结伴上车的学生仔子们 用笑声打破车厢的沉默 大人眼里的晚高峰 是给放学后快乐😀时光的加时 下车的学生匆匆起身带起 一阵熟悉的栀子香于💓 是关于校园的记忆 开始零零散散地闪现 放学后集合的秘密基地/跟着城…...

C++ | 认识标准库string和vector

本文概要 本篇文章主要介绍C的标准库类型string和vector,文中描述和代码示例很详细,看完即可掌握,感兴趣的小伙伴快来一起学习吧。 🌟🌟🌟个人简介 🌟🌟🌟 ☀️大家好&a…...

JAVA面试宝典: SpringCloud知识点(通俗易懂易背)

1、什么是 Spring Cloud? Spring Cloud 是基于 Spring Boot 的微服务架构开发工具箱,提供了在分布式系统中构建可靠的、弹性的、灵活的应用所需的大多数工具。Spring Cloud 中包含的子项目如下: Spring Cloud Config:配置管理工具…...

es学习笔记

集群环境下数据往哪个节点放? 路由计算:hash(id) %主分片的数量 集群环境下查数据怎么查? 分配控制:访问任何一个节点都能获取数据,随机访问到的这个节点称为协调节点(访问了当前节点,不一定从当前节点…...

SAS学习第9章:卡方检验之适合性检验与独立性检验

卡方检验就是统计样本的实际观测值与理论推断值之间的偏离程度,实际观测值与理论推断值之间的偏离程度就决定卡方值的大小,如果卡方值越大,二者偏差程度越大;反之,二者偏差越小;若两个值完全相等时&#xf…...

马斯克爆料Twitter裁了八成员工;OpenAI CEO:GPT-5根本不存在;小鹏被曝年终奖打0.5折 | AI一周资讯

来源: AI前线 微信号:ai-front 整理 | 凌敏 微软宣布开源 Deep Speed Chat;消息称软银旗下 Arm 启动赴美 IPO;国家网信办出台生成式 AI 管理办法;前理想 AI 芯片一号位骄旸加入三星,负责组建 GPU 团队…… 资 讯 Op…...

ASEMI代理ADG1408YRUZ-REEL7原装ADI车规级ADG1408YRUZ-REEL7

编辑:ll ASEMI代理ADG1408YRUZ-REEL7原装ADI车规级ADG1408YRUZ-REEL7 型号:ADG1408YRUZ-REEL7 品牌:ADI /亚德诺 封装:TSSOP-16 批号:2023 安装类型:表面贴装型 引脚数量:16 类型&#…...

phpstudy本地环境搭建图文教程

作者:Eason_LYC 悲观者预言失败,十言九中。 乐观者创造奇迹,一次即可。 一个人的价值,在于他所拥有的。可以不学无术,但不能一无所有! 技术领域:WEB安全、网络攻防 关注WEB安全、网络攻防。我的…...

【UE 控件蓝图】菜单及功能实现

素材资源连接:百度网盘 请输入提取码 密码:fvcw 效果 步骤 1. 创建蓝图,父类为“HUD” 命名为“MainMenuHUD”并打开 在事件图表中添加如下节点: 2. 创建控件蓝图,命名为“MainMenuWidget” 此时在“MainMenuHUD”的…...

Java 并发编程面试题——Future

目录 1.什么是 Future 模式?Java 中是如何实现的?2.Callable、Future 与 FutureTask 分别是什么?2.1.Callable 接口2.2.Future 接口2.3.FutureTask 类 3.CompletableFuture 类有什么用? 1.什么是 Future 模式?Java 中是…...

SpringBoot 介绍

1.简介 SpringBoot最开始基于Spring4.0设计,是由Pivotal公司提供的框架。 SpringBoot发展史: 2003年Rod Johnson成立Interface公司,产品是SpringFramework2004年,Spring框架开源,公司改名为Spring Source2008年&…...

自动驾驶作业手册

1 总 则 目的为保障港口内自动驾驶车辆安全使用,预防和减少事故,保护人民生命和财产安全,促进港口内业务开展。 含义和范围港口内自动驾驶车辆,是指电脑驾驶车辆,为一种运输动力的无人地面载具,与有人驾驶车辆不同,其具备不需要人类操作即可以感测其环境及导航功能,能…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

ardupilot 开发环境eclipse 中import 缺少C++

目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...