SpringMVC系列八: 手动实现SpringMVC底层机制-第三阶段

手动实现SpringMVC底层机制

  • 实现任务阶段六
    • 🍍完成控制器方法获取参数-@RequestParam
      • 1.🥦将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用
      • 2.🥦在方法形参处, 指定 @RequestParam, 将对应的实参封装到参数数组, 进行反射调用
      • 3.🥦在方法形参 没有指定 @RequestParam, 按照默认参数名获取值, 进行反射调用

在这里插入图片描述

实现任务阶段六

🍍完成控制器方法获取参数-@RequestParam

功能说明: 自定义@RequestParam 和 方法参数名获取参数
完成: 将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用
完成: 在方法参数 指定 @RequestParam 的参数封装到参数数组, 进行反射调用
完成: 在方法参数 没有指定 @RequestParam, 按照默认参数名获取值, 进行反射调用

示意图[分析说明]
在这里插入图片描述

1.🥦将 方法的 HttpServletRequest 和 HttpServletResponse 参数封装到数组, 进行反射调用

修改ZzwDispatcherServletexecuteDispatcher()方法

//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//说明用户请求的路径/资源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射调用控制器的方法//目标将: HttpServletRequest 和 HttpServletResponse 封装到参数数组//1. 得到目标方法的所有形参参数信息[返回对应的数组]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 创建一个参数数组[对应实参数组], 在后面反射调用目标方法时, 会使用到Object[] params = new Object[parameterTypes.length];//3.遍历parameterTypes形参数组, 根据形参数组信息, 将实参填充到实参数组中for (int i = 0; i < parameterTypes.length; i++) {//取出每一个形参"类型"Class<?> parameterType = parameterTypes[i];//如果这个形参类型是HttpServletRequest, 将request填充到params//在原生的SpringMVC中, 是按照类型来进行匹配的, 老师这里简化, 使用名字来匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if("HttpServletResponse".equals(parameterType.getSimpleName())){params[i] = response;}}/*** 1.下面这样写, 其实是针对目标方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()*              .invoke(zzwHandler.getController(), request, response)* 2.这里我们准备将需要传递给目标方法的 实参 => 封装到参数数组*   => 然后以反射调用的方式传递给目标方法* 源码: public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}

2.🥦在方法形参处, 指定 @RequestParam, 将对应的实参封装到参数数组, 进行反射调用

1.MonsterService新添方法findMonsterByName

public interface MonsterService {//增加方法, 通过传入的name, 返回对应的monster列表public List<Monster> findMonsterByName(String name);
}

MonsterServiceImpl将其实现

@Service
public class MonsterServiceImpl implements MonsterService {public List<Monster> findMonsterByName(String name) {//这里我们模拟数据->DBList<Monster> monsters = new ArrayList<Monster>();monsters.add(new Monster(100, "牛魔王", "芭蕉扇", 400));monsters.add(new Monster(200, "汤姆猫", "抓老鼠", 200));monsters.add(new Monster(300, "红孩儿", "三昧真火", 100));monsters.add(new Monster(400, "黄袍怪", "吐烟雾", 300));monsters.add(new Monster(500, "白骨精", "美人计", 800));//创建集合并且返回查询到的monster集合List<Monster> findMonsters = new ArrayList<Monster>();//遍历monsters集合, 返回满足条件的对象for (Monster monster : monsters) {if (monster.getName().contains(name)) {findMonsters.add(monster);}}return findMonsters;}
}

2.com.zzw.zzwspringmvc.annotation下新建@RequestParam

/*** @author 赵志伟* @version 1.0* RequestParam 注解 标注在目标方法的参数上, 表示对应http请求的参数*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)//runtime表示在反射时可以拿到这个注解
@Documented
public @interface RequestParam {String value() default "";
}

3.ZzwDispatcherServlet增添代码

注意点
1)method.getParameters(): 得到所有的形参
2)method.getParameterTypes(): 得到形参的类型

//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//说明用户请求的路径/资源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射调用控制器的方法//目标将: HttpServletRequest 和 HttpServletResponse 封装到参数数组//1. 得到目标方法的所有形参参数信息[对应的数组]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 创建一个参数数组[对应实参数组], 在后面反射调用目标方法时, 会使用到Object[] params = new Object[parameterTypes.length];//3.遍历parameterTypes形参数组, 根据形参数组信息, 将实参填充到实参数组中for (int i = 0; i < parameterTypes.length; i++) {//取出每一个形参类型Class<?> parameterType = parameterTypes[i];//如果这个形参是HttpServletRequest, 将request填充到params//在原生的SpringMVC中, 是按照类型来进行匹配的, 老师这里简化, 使用名字来匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {params[i] = response;}}//将http请求参数封装到params数组中, 老韩提示, 要注意填充实参的时候, 顺序问题 👈//1.获取http请求的参数集合//老韩解读//2.返回的Map<String, String[]> String: 表示http请求的参数名//  String[]: 表示http请求的参数值, 想一下为什么是数组?//http://localhost:8080/monster/find?name=牛魔王&hobby=打篮球&hobby=喝酒&hobby=吃肉(防止有类似checkbox)Map<String, String[]> parameterMap =request.getParameterMap();//3.遍历parameterMap, 将请求参数, 按照顺序填充到实参数组for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {//取出key. 这个name就是对应请求的参数名String name = entry.getKey();//说明: 只考虑提交的数据是单值的情况, 即不考虑类似checkbox提交的数据//     老师这里做了简化, 如果考虑多值情况, 也不难String value = entry.getValue()[0];//我们得到请求的参数对应我们目标方法的第几个形参, 然后将其填充//这里专门编写一个方法, 得到请求参数对应的是第几个形参//1. API 2.java内力真正增加 3.老韩忠告..int indexRequestParameterIndx =getIndexRequestParameterIndex(zzwHandler.getMethod(), name);if (indexRequestParameterIndx != -1) {//找到对应的位置params[indexRequestParameterIndx] = value;} else {//说明并没有找到@RequestParam注解对应的参数, 就会使用默认的机制进行匹配[待...]//一会再写}}                                                                 👈/*** 1.下面这样写, 其实是针对目标方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()*              .invoke(zzwHandler.getController(), request, response)* 2.这里我们准备将需要传递给目标方法的 实参=> 封装到参数数组=> 然后以反射调用的方式传递给目标方法* public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}//编写方法, 返回请求参数是目标方法的第几个形参/*** @param method 目标方法* @param name   请求的参数名* @return 是目标方法的第几个形参*/
public int getIndexRequestParameterIndex(Method method, String name) {//1.得到method的所有形参参数Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {//取出当前的参数Parameter parameter = parameters[i];//判断parameter是不是有@RequestParam注解boolean annotationPresent = parameter.isAnnotationPresent(RequestParam.class);if (annotationPresent) {//说明有@RequestParam//取出当前这个参数的@RequestParam(value = "xxx")RequestParam requestParamAnnotation =parameter.getAnnotation(RequestParam.class);String value = requestParamAnnotation.value();//这里就是匹配的比较if (name.equals(value)) {return i;//找到请求的参数, 对应的目标方法的形参的位置}}}//如果没有匹配成功, 就返回-1return -1;
}

4.MonsterController增加如下方法

//增加方法, 通过name返回对应的monster对象
@RequestMapping(value = "/monster/find")
public void findMonsterByName(HttpServletRequest request,HttpServletResponse response,@RequestParam(value = "name") String name) {//设置返回编码和返回类型response.setContentType("text/html;charset=utf8");System.out.println("---接收到的name---" + name);👈StringBuilder content = new StringBuilder("<h1>妖怪列表信息</h1>");//单线程使用StringBuildercontent.append("<table border='1px' width='500px' style='border-collapse:collapse'>");//调用monsterServiceList<Monster> monsters = monsterService.findMonsterByName(name);👈for (Monster monster : monsters) {content.append("<tr><td>" + monster.getId() + "</td><td>" + monster.getName() + "</td><td>"+ monster.getAge() + "</td><td>" + monster.getSkill() + "</td></tr>");}content.append("</table>");//获取writer返回信息try {response.getWriter().write(content.toString());} catch (IOException e) {throw new RuntimeException(e);}
}

5.测试
在这里插入图片描述在这里插入图片描述

3.🥦在方法形参 没有指定 @RequestParam, 按照默认参数名获取值, 进行反射调用

1.去掉MonsterControllerfindMonsterByName()方法的name字段的@RequestParam注解

//增加方法, 通过name返回对应的monster对象
@RequestMapping(value = "/monster/find")
public void findMonsterByName(HttpServletRequest request,HttpServletResponse response,String name) {.....
}

2.ZzwDispatcherServletexecuteDispatcher()else分支内增加如下代码, 并增加方法getParameterNames

//编写方法, 完成分发请求任务
private void executeDispatcher(HttpServletRequest request,HttpServletResponse response) {try {ZzwHandler zzwHandler = getZzwHandler(request);if (zzwHandler == null) {//说明用户请求的路径/资源不存在response.getWriter().print("<h1>404 NOT FOUND!</h1>");} else {//匹配成功, 反射调用控制器的方法//目标将: HttpServletRequest 和 HttpServletResponse 封装到参数数组//1. 得到目标方法的所有形参参数信息[对应的数组]Class<?>[] parameterTypes =zzwHandler.getMethod().getParameterTypes();//2. 创建一个参数数组[对应实参数组], 在后面反射调用目标方法时, 会使用到Object[] params = new Object[parameterTypes.length];//3.遍历parameterTypes形参数组, 根据形参数组信息, 将实参填充到实参数组中for (int i = 0; i < parameterTypes.length; i++) {//取出每一个形参类型Class<?> parameterType = parameterTypes[i];//如果这个形参是HttpServletRequest, 将request填充到params//在原生的SpringMVC中, 是按照类型来进行匹配的, 老师这里简化, 使用名字来匹配if ("HttpServletRequest".equals(parameterType.getSimpleName())) {params[i] = request;} else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {params[i] = response;}}//将http请求参数封装到params数组中, 老韩提示, 要注意填充实参的时候, 顺序问题//1.获取http请求的参数集合//老韩解读//http://localhost:8080/monster/find?name=牛魔王&hobby=打篮球&hobby=喝酒&hobby=吃肉(防止有类似checkbox)//2.返回的Map<String, String[]> String: 表示http请求的参数名//  String[]: 表示http请求的参数值, 为什么是数组//Map<String, String[]> parameterMap = request.getParameterMap();//2.遍历parameterMap, 将请求参数, 按照顺序填充到实参数组for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {//取出key. 这个name就是对应请求的参数名String name = entry.getKey();//说明: 只考虑提交的数据是单值的情况, 即不考虑类似checkbox提交的数据//     老师这里做了简化, 如果考虑多值情况, 也不难String value = entry.getValue()[0];//我们得到请求的参数对应我们目标方法的第几个形参, 然后将其填充//这里专门编写一个方法, 得到请求参数对应的是第几个形参//1. API 2.java内力真正增加 3.老韩忠告..int indexRequestParameterIndx =getIndexRequestParameterIndex(zzwHandler.getMethod(), name);if (indexRequestParameterIndx != -1) {//找到对应的位置params[indexRequestParameterIndx] = value;
👉👉👉👉👉👉👉} else {//说明并没有找到@RequestParam注解对应的参数, 就会使用默认的机制进行配置[待...]//思路 														//1.得到目标方法的所有形参的名称, 而不是形参类型的名称-专门编写一个方法获取形参名//2.对得到的目标方法的所有形参名进行遍历, 如果匹配就把当前请求的参数值, 填充到paramsList<String> parameterNames = getParameterNames(zzwHandler.getMethod());for (int i = 0; i < parameterNames.size(); i++) {//如果请求参数名和目标方法的形参名一样, 说明匹配成功if (name.equals(parameterNames.get(i))) {params[i] = value;//填充到实参数组break;}}                  									}}/*** 1.下面这样写, 其实是针对目标方法 m(HttpServletRequest request, HttpServletResponse response)* zzwHandler.getMethod()*              .invoke(zzwHandler.getController(), request, response)* 2.这里我们准备将需要传递给目标方法的 实参=> 封装到参数数组=> 然后以反射调用的方式传递给目标方法* public Object invoke(Object var1, Object... var2)...*/zzwHandler.getMethod().invoke(zzwHandler.getController(), params);}} catch (Exception e) {throw new RuntimeException(e);}
}//编写方法, 得到目标方法的所有形参的名称, 并放入到集合中返回/*** @param method 目标方法* @return 所有形参的名称, 并放入到集合中返回*/
public List<String> getParameterNames(Method method) {List<String> parametersList = new ArrayList<String>();//获取到所有的参数名称, 而不是参数类型的名称//这里有一个小细节-->在默认情况下 parameter.getName()//得到的名字不是形参真正的名字, 而是 [arg0, arg1, arg2...]//, 这里我们要引入一个插件, 使用java8的特性, 这样才能解决Parameter[] parameters = method.getParameters();//遍历parameters, 取出名字, 放入parametersListfor (Parameter parameter : parameters) {parametersList.add(parameter.getName());}System.out.println("目标方法的形参参数列表=" + parametersList);return parametersList;
}

3.parameter.getName()得到的名字不是形参真正的名字, 而是 [arg0, arg1, arg2…].
Parameter[] parameters = method.getParameters();
parameters=[javax.servlet.http.HttpServletRequest arg0,
                     javax.servlet.http.HttpServletResponse arg1,
                     java.lang.String arg2]

parameter.getName() ⇒ [arg0, arg1, arg2]

我们需要引入一个插件.
pom.xmlbuild节点内插入以下代码 -parameters, 点击右上角刷新

<build><finalName>zzw-springmvc2(你的文件名)</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.7.0</version><configuration><source>1.8</source><target>1.8</target><compilerArgs><arg>-parameters</arg></compilerArgs><encoding>utf-8</encoding></configuration></plugin></plugins>
</build>

3.1点击maven管理, Lifecycle目录下, clean项目, 重启(不是重新部署)tomcat.
在这里插入图片描述

5.测试
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/864020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis缓存问题二、缓存雪崩

缓存雪崩 缓存雪崩&#xff1a;是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 缓存雪崩的解决方案&#xff1a; 给不同的Key的TTL添加随机值利用Redis集群提高服务的可用性给缓存业务添加降级限流策略…

sql-语句

文章目录 SQL语句的学习sql是什么sql的内置命令sql的种类sql mode库&#xff0c;表属性介绍&#xff1a;字符集&#xff0c;存储引擎列的数据类型&#xff1a;数字&#xff0c;字符串&#xff0c;时间列的约束DDL: 数据定义语言库表 Online DDL(ALGORITHM) *DML :数据操纵语言资…

SpringBoot实现文章点赞功能

提示&#xff1a;今日是2024年的6月30日&#xff0c;未来的你看到这篇文章&#xff0c;希望你依旧快乐 文章目录 前言 首先在这里前缀部分我就不做要求了,比如说登录信息什么的 数据库表格 这里实现点赞功能&#xff0c;主要是围绕论坛项目完成的 user_info代表用户信息表 for…

SprongBoot3整合Knife4j

大家好&#xff0c;我是晓凡。 写在前面 在上一篇文章&#xff0c;我们详细介绍了SpringBoot3 怎么整合SpringDoc实现在线接口文档。但是&#xff0c;有不少小伙伴 都觉得接口界面太丑了。有没有什么更美观一点的UI界面呢&#xff1f; 当然是有的了&#xff0c;毕竟这是一…

抖音直播自动点赞脚本:让点赞变得简单

抖音直播自动点赞脚本&#xff1a;让点赞变得简单 简介 点赞是社交媒体上表达喜爱的一种方式&#xff0c;尤其在抖音这样的平台上&#xff0c;点赞不仅能够增加主播的人气&#xff0c;还能鼓励他们创作更多优质内容。然而&#xff0c;手动点赞往往既耗时又费力。为了解决这个…

云服务出现故障这样处理

无法连接云服务器 服务器远程无法连接时&#xff0c;可通过7ECloud控制台进行连接。 常见故障现象 1、ping不通 2、ping丢包 3、部分端口telnet不通 4、全部端口telnet不通 5、广告、弹窗植入 6、域名无法访问IP访问正常 常见故障原因 1、云服务器过期、关机或者EIP被…

深度学习基准模型Transformer

深度学习基准模型Transformer 深度学习基准模型Transformer&#xff0c;最初由Vaswani等人在2017年的论文《Attention is All You Need》中提出&#xff0c;是自然语言处理&#xff08;NLP&#xff09;领域的一个里程碑式模型。它在许多序列到序列&#xff08;seq2seq&#xf…

恭喜了!全体前端彻底狂欢吧!这个好消息来得太及时!

在这个快速变化的科技时代&#xff0c;作为独立开发者&#xff0c;你是否常常被繁琐的开发任务压得喘不过气&#xff1f;前端开发要操心后端的各种服务搭建和接口开发&#xff0c;这些琐碎而耗时的工作常常让人头大。但现在&#xff0c;你可以松一口气了&#xff0c;因为MemFir…

【ARM-Linux篇】项目:智能家居

一、项目概述 •项目功能 通过语音控制客厅灯、卧室灯、风扇、人脸识别开门等,可以进行火灾险情监测,可以并且实现Sockect发送指令远程控制各类家电等 •项目描述 全志H616通过串口连接各模块硬件,检测语音的识别结果,分析语音识别的结果来对家电设备进行控制。摄像头拍…

视频上面怎样编辑文字?4种视频编辑文字方法分享

视频已成为我们日常生活中不可或缺的一部分。无论是社交分享、商业宣传还是个人记录&#xff0c;视频都以其直观、生动的特点吸引着观众的眼球。然而&#xff0c;一个优质的视频&#xff0c;除了画面和音效&#xff0c;文字编辑也是提升观看体验的关键。那么&#xff0c;如何在…

Webpack: 并行构建

概述 受限于 Node.js 的单线程架构&#xff0c;原生 Webpack 对所有资源文件做的所有解析、转译、合并操作本质上都是在同一个线程内串行执行&#xff0c;CPU 利用率极低&#xff0c;因此&#xff0c;理所当然地&#xff0c;社区出现了一些以多进程方式运行 Webpack&#xff0…

Redis学习——Redisson 分布式锁集成及其简单使用

文章目录 引言1. Redisson概述1.1 Redisson的基本概念1.2 Redisson的主要功能1.3 Redisson的优点 2. 开发环境3. Redisson的安装与配置3.1 添加依赖3.2 配置Redisson 4. 使用Redisson4.1 可重入锁4.1.1 可重入锁的概念4.1.2 可重入锁的实现原理4.1.3 简单使用锁的获取和释放 4.…

在线疫苗预约小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;工作人员管理&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;疫苗管理&#xff0c;论坛管理&#xff0c;公告管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;公告&#xff0c;疫苗&…

【FreeRTOS】空闲任务

目录 空闲任务及其钩子函数介绍使用钩子函数的前提 实际操作任务如何退出&#xff1f;IDLE函数 空闲任务及其钩子函数 介绍 空闲任务(Idle任务)的作用之一&#xff1a;释放被删除的任务的内存。 除了上述目的之外&#xff0c;为什么必须要有空闲任务? 这是一个良好的程序&…

Linux基础 - DHCP 动态管理主机地址

目录 零. 简介 一. 部署 dhcpd 二. 提高安全性 零. 简介 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff09;是一种网络协议&#xff0c;用于动态地为主机分配 IP 地址、子网掩码、默认网关、DNS 服务器等网络配置信息。 其主要…

鸿蒙 HarmonyOs 动画效果 快速入门

一、理论 1.1 animation属性 名称参数类型必填描述durationnumber否设置动画时长&#xff0c;默认值&#xff1a;1000&#xff0c;单位&#xff1a;毫秒temponumber否动画播放速度。数值越大&#xff0c;速度越快&#xff0c;默认为1curvestring | Curve否 设置动画曲线。 默…

C语言--vs使用调试技巧

1.什么是bug? 1.产品说明书中规定要做的事情&#xff0c;而软件没有实现。 2.产品说明书中规定不要做的事情&#xff0c;而软件确实现了。 3.产品说明书中没有提到过的事情&#xff0c;而软件确实现了。 4.产品说明书中没有提到但是必须要做的事情&#xff0c;软件确没有实…

vue3中使用弹幕组件vue-danmaku

1、最开始使用的是vue3-marquee&#xff0c;后面发现一直有一个bug无法解决&#xff0c;就是鼠标hover到第一个弹幕上字体就会变粗&#xff0c;已经提了issue给作者&#xff0c;但是目前还未答复&#xff0c;所以就换了方案。 地址如下&#xff1a; https://github.com/megasa…

stable-diffusion-webui-colab搭建SadTalker由图生成视频人

在这里选择一个stable-diffusion-webui-colab ​​​​​​​​​GitHub - camenduru/stable-diffusion-webui-colab: stable diffusion webui colab 这里我选择是&#xff1a; https://colab.research.google.com/github/camenduru/stable-diffusion-webui-colab/blob/main…

【Python时序预测系列】基于LSTM实现多输入多输出单步预测(案例+源码)

这是我的第312篇原创文章。 一、引言 单站点多变量输入多变量输出单步预测问题----基于LSTM实现。 多输入就是输入多个特征变量 多输出就是同时预测出多个标签的结果 单步就是利用过去N天预测未来1天的结果 二、实现过程 2.1 读取数据集 dfpd.read_csv("data.csv&qu…