手写SpringMVC之调度器DispatcherServlet

DispatcherServlet:分发、调度

根据上一节,已经实现了将controller的方法添加到容器中,而DispatcherServlet的作用就是接收来自客户端的请求,然后通过URI的组合,来找到对应的@RequestMapping注解的方法,调用对应的方法,最后返回响应

image-20240627203051007

第一步:获取URI,根据URI来匹配对应的BeanDefinition

String requestURI = req.getRequestURI();Map<String, BeanDefinition<?>> maps = BeanContainer.getMaps();//通过匹配URI来找到对应的BeanDefinition
BeanDefinition<?> beanDefinition = maps.get(requestURI);
if (beanDefinition == null) {throw new FrameWorkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(), ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());
}

第二步:获取容器中的BeanDefinition的MethodDefinition和ParameterDefinition

//获取对应的controller类对象
Object t = beanDefinition.getT();
MethodDefinition methodDefinition = beanDefinition.getMethodDefinition();
//获取方法对象
Method method = methodDefinition.getMethod();
method.setAccessible(true);
//获取参数列表
List<ParameterDefinition> parameterDefinitions = methodDefinition.getParameters();

第三步:调用对应的方法

Object[] args;
Model model = new Model();try {args = handlerParameterArgs(parameterDefinitions, req, resp, model);//调用Controller层里某个方法Object returnVal = method.invoke(t, args);if (returnVal != null) {//处理返回值handlerReturnVal(methodDefinition, returnVal, req, resp, model);}
} catch (Exception e) {System.out.println(Arrays.toString(e.getStackTrace()));;
}
handlerParameterArgs:将调用的方法的参数列表与请求数据适配
/*** 集中处理参数的函数,通过判断参数的类型,对不同类型的参数进行处理,包括* 1. 常见参数类型:八大基本数据类型及其包装类 + String* 2. 数组类型* 3. HttpServletRequest 类型* 4. httpServletResponse 类型* 5. List<?> 类型* 6. 自定义类型** @param parameterDefinitions 参数描述对象列表(从controller的方法下抽取出来的)* @param req                  请求对象* @param resp                 响应对象* @param model                数据体(应该是,里面是Map,key为数据名,value为数据体,最后通过装载到request对象转发出去)* @return 参数列表 Object[] args*/
public Object[] handlerParameterArgs(List<ParameterDefinition> parameterDefinitions, HttpServletRequest req, HttpServletResponse resp, Model model) throws ClassNotFoundException {if (parameterDefinitions == null) {return null;}//实际参数的列表Object[] args = new Object[parameterDefinitions.size()];//将请求中的参数添加到args中for (ParameterDefinition parameterDefinition : parameterDefinitions) {String name = parameterDefinition.getParameterName();//参数名Class<?> type = parameterDefinition.getType();//参数类型int index = parameterDefinition.getIndex();//参数下标if (judgeTypeIsJavaOrNot(type)) {//常见数据类型handlerJavaType(req, name, type, args, index);} else if (type == HttpServletRequest.class) {//请求类型args[index] = req;} else if (type == HttpServletResponse.class) {//相应类型args[index] = resp;} else if (type == String[].class) {//数组类型String[] parameterValues = req.getParameterValues(name);args[index] = parameterValues;} else if (type == List.class) {//集合类型handlerListType(parameterDefinition, req, args, index);} else if (type == Model.class) {//Model类型args[index] = model;} else {//自定义类型handlerOtherType(parameterDefinition, req, args, index);}}return args;
}
judgeTypeIsJavaOrNot:判断方法参数是否是常见数据类型

常见参数类型:八大基本数据类型及其包装类 + String

private static final Class<?>[] COMMON_CLASSES = new Class[]{byte.class, short.class, int.class, long.class, float.class, double.class, char.class,Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Character.class,String.class};
/*** 判断参数的类型是不是常见类型 [COMMON_CLASSES]** @param type 参数类型* @return 是否为常见类型*/
public boolean judgeTypeIsJavaOrNot(Class<?> type) {for (Class<?> clazz : COMMON_CLASSES) {if (type == clazz) {return true;}}return false;
}
handlerJavaType:处理常见参数
/*** 处理不同的常见数据类型,将其转化为对应的数据并添加到参数列表对应的位置** @param req   请求对象* @param name  字段名* @param type  字段类型* @param args  参数列表* @param index 参数在方法中的下标*/
public void handlerJavaType(HttpServletRequest req, String name, Class<?> type, Object[] args, int index) {String parameter = req.getParameter(name);if (type == byte.class || type == Byte.class) {args[index] = Byte.parseByte(parameter);} else if (type == short.class || type == Short.class) {args[index] = Short.parseShort(parameter);} else if (type == int.class || type == Integer.class) {args[index] = Integer.parseInt(parameter);} else if (type == long.class || type == Long.class) {args[index] = Long.parseLong(parameter);} else if (type == float.class || type == Float.class) {args[index] = Float.parseFloat(parameter);} else if (type == double.class || type == Double.class) {args[index] = Double.parseDouble(parameter);} else if (type == char.class || type == Character.class) {args[index] = parameter.toCharArray()[0];} else if (type == boolean.class || type == Boolean.class) {args[index] = Boolean.parseBoolean(parameter);}if (type == String.class) {args[index] = parameter;}
}
handlerListType:处理List参数
/*** 处理方法的参数是集合类型的方法,如果参数是List集合,那么要将List中的泛型取出来并设置对应的属性* 最后将泛型对应的对象添加到List中,再将List添加到参数列表中** @param parameterDefinition 参数描述对象(包含泛型的类型)* @param req                 请求对象* @param args                参数列表* @param index               参数对应的下标*/
public void handlerListType(ParameterDefinition parameterDefinition, HttpServletRequest req, Object[] args, int index) throws ClassNotFoundException {Type[] types = parameterDefinition.getTypes();//参数的泛型列表Type genericType = types[0];//泛型列表String typeName = genericType.getTypeName();//泛型的名称 cn.cnmd.pojo.UserList<Object> list = new ArrayList<>();Map<String, String[]> parameterMap = req.getParameterMap();Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();for (Map.Entry<String, String[]> entry : entries) {String key = entry.getKey();String fieldValue = entry.getValue()[0];int i = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.indexOf("]")));String fieldName = key.substring(key.indexOf(".") + 1);Class<?> aClass = Class.forName(typeName);Object o = null;try {o = list.get(i);} catch (IndexOutOfBoundsException e) {//该集合下标上没有元素try {o = aClass.newInstance();//创建对象list.add(o);} catch (InstantiationException | IllegalAccessException ex) {throw new RuntimeException(ex);}}try {BeanUtils.setProperty(o, fieldName, fieldValue);} catch (IllegalAccessException | InvocationTargetException e) {throw new RuntimeException(e);}}args[index] = list;
}
handlerOtherType:处理自定义参数
/*** 处理自定义类型** @param parameterDefinition 参数描述对象* @param req                 请求对象* @param args                参数列表* @param index               参数下标*/
public void handlerOtherType(ParameterDefinition parameterDefinition, HttpServletRequest req, Object[] args, int index) {try {Object obj;//如果参数上带有@RequestBody注解则会将JSON字符串转化为对象if (parameterDefinition.isRequestBodyHasOrNot()) {BufferedReader reader = req.getReader();StringBuffer sb = new StringBuffer();char[] cs = new char[1024];int len;while ((len = reader.read(cs)) != -1) {sb.append(cs, 0, len);}//String --> Objectobj = objectMapper.readValue(sb.toString(), parameterDefinition.getType());} else { //如果不带@RequestBody注解,则正常当作自定义对象处理obj = parameterDefinition.getType().newInstance();Map<String, String[]> parameterMap = req.getParameterMap();BeanUtils.populate(obj, parameterMap);}args[index] = obj;} catch (InstantiationException | IllegalAccessException | InvocationTargetException | IOException e) {throw new RuntimeException(e);}
}
handlerRequestVal:将Model中的k-v添加到request请求体中
/*** 将model数据对象装载到request对象中** @param request 请求体对象* @param map     model数据对象的map(k-v存储了需要传递给前端的数据)*/
public void handlerRequestVal(HttpServletRequest request, Map<String, Object> map) {Set<Map.Entry<String, Object>> entries = map.entrySet();for (Map.Entry<String, Object> entry : entries) {String key = entry.getKey();Object value = entry.getValue();request.setAttribute(key, value);}
}
handlerReturnVal:返回响应的总调度(按照方法的返回类型)
  1. 如果是String类型,说明返回值是一个URI,ps:‘/user/index.jsp’,就将数据加载到request对象中,跳转到该页面
  2. 如果是ModelAndView类型,说明返回值是一个ModelAndView对象[ Model数据体对象 + URI],再将数据加载到request对象中,通过ModelANdView.getViewName()跳转
  3. 如果是JSON数据[方法上带有@ResponseBody注解],就直接将数据发送到前端
/*** 处理返回值的函数,通过判断URI调用对应的方法的返回值类型,选择不同返回逻辑* 1.如果是String类型,说明返回值是一个URI,ps:'/user/index.jsp',就将数据加载到request对象中,跳转到该页面* 2.如果是ModelAndView类型,说明返回值是一个ModelAndView对象[ Model数据体对象 + URI],再将数据加载到request对象中,通过ModelANdView.getViewName()跳转* 3.如果是JSON数据[方法上带有@ResponseBody注解],就直接将数据发送到前端** @param methodDefinition 方法描述对象* @param returnVal        调用方法的返回值对象* @param request          请求体对象* @param response         响应体对象* @param model            数据体对象*/
public void handlerReturnVal(MethodDefinition methodDefinition, Object returnVal, HttpServletRequest request, HttpServletResponse response, Model model) throws ServletException, IOException {//如果返回的是一个String, 代表直接跳转,ps:'user/login2.action'if (returnVal.getClass() == String.class) {handlerRequestVal(request, model.getMap());jumpPage(returnVal, request, response);//如果返回的是ModelAndView对象,那么代表跳转的地址作为属性被注入到ModelAndView对象中,通过modelAndView.getViewName()跳转} else if (returnVal.getClass() == ModelAndView.class) {ModelAndView modelAndView = (ModelAndView) returnVal;handlerRequestVal(request, modelAndView.getMap());jumpPage(modelAndView.getViewName(), request, response);//如果这个方法上有@REsponseBody注解,则直接将returnVal转化为JSON字符串传出} else if (methodDefinition.isResponseBodyHasOrNot()) {String jsonStr = objectMapper.writeValueAsString(returnVal);sendResponse(jsonStr, response);}}
jumpPage:跳转页面
/*** 页面跳转函数** @param uri      需要跳转的URI* @param request  请求体对象(里面携带了从model装载的数据)* @param response 相应体对象*/
public void jumpPage(Object uri, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String view = (String) uri;request.getRequestDispatcher(view).forward(request, response);
}
sendResponse:直接返回响应
/*** 向前端发送JSON数据* @param jsonStr JSON字符串* @param response 相应提对象*/
public void sendResponse( String jsonStr,HttpServletResponse response) throws IOException {response.getWriter().write(jsonStr);
}

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

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

相关文章

JS+CSS+HTML项目-中国国家图书馆

页面做的不多&#xff0c;CSS效果请看哔哩哔哩

SpringBoot整合Dubbo的快速使用教程

目录 一、什么是Dubbo? 二、SpringBoot整合Dubbo 1、父工程引入依赖 2、各个Dubbo服务子模块引入依赖 3、服务提供者 &#xff08;1&#xff09;启动类添加注解EnableDubbo &#xff08;2&#xff09;服务类添加注解DubboService &#xff08;3&#xff09;配置文件…

再有人问你WebSocket为什么牛逼,就把这篇文章发给他!

点赞再看&#xff0c;Java进阶一大半 2008年6月诞生了一个影响计算机世界的通信协议&#xff0c;原先需要二十台计算机资源才能支撑的业务场景&#xff0c;现在只需要一台&#xff0c;这得帮"抠门"老板们省下多少钱&#xff0c;它就是大名鼎鼎的WebSocket协议。很快在…

【数智化人物展】数势科技创始人兼CEO黎科峰:数智化时代To B软件行业面临颠覆与重塑...

黎科峰 本文由数势科技创始人兼CEO黎科峰投递并参与由数据猿联合上海大数据联盟共同推出的《2024中国数智化转型升级先锋人物》榜单/奖项评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 2020年&#xff0c;对我而言&#xff0c;是职业生涯中的一个重大转折点。在全球新…

《后端程序猿 · Caffeine 本地缓存》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻一周&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

满足GMSL静电防护要求的方案

什么是GMSL&#xff1f;它是做什么用的&#xff1f;它有什么优点&#xff1f;设计GMSL防静电有啥难度&#xff1f; 带着这些疑问我们先了解下什么是GMSL。 一&#xff0e;简述 GMSL GMSL&#xff08;Gigabit Multimedia Serial Link&#xff09;即千兆多媒体串行链路&#xf…

MySQL中的DDL语句

第一题 输入密码登录mysql&#xff0c;创建数据库zoo&#xff0c;转换到zoo数据库&#xff0c; mysql> create database zoo character set gbk; mysql> use zoo查看创建数据库zoo信息 mysql> show create database zoo;删除数据库zoo mysql> drop database zo…

TP8/6 更改后台入口地址admin改为myadmin 隐藏真实后台网址

原来www.xxx.com/admin 改后www.xxx.com/myadmin config/app.php // 应用映射&#xff08;自动多应用模式有效&#xff09;app_map > [admintest>admin], 官方文档&#xff1a;ThinkPHP官方手册

在C#/Net中使用Mqtt

net中MQTT的应用场景 c#常用来开发上位机程序&#xff0c;或者其他一些跟设备打交道比较多的系统&#xff0c;所以会经常作为拥有数据的终端&#xff0c;可以用来采集上传数据&#xff0c;而MQTT也是物联网常用的协议&#xff0c;所以下面介绍在C#开发中使用MQTT。 安装MQTTn…

TK养号工具开发会用上的源代码科普!

在当今数字化时代&#xff0c;社交媒体平台的崛起使得网络账号的维护与管理变得日益重要&#xff0c;其中&#xff0c;TK作为一款备受欢迎的社交媒体平台&#xff0c;吸引了大量用户。 在TK上进行账号养护&#xff0c;即通过各种方式提升账号权重、增加曝光量&#xff0c;已成…

小阿轩yx-LVS+Keepalived群集

小阿轩yx-LVSKeepalived群集 Keepalived 双机热备份基础知识 起初是专门针对 LVS 设计的一款强大的辅助工具主要用来提供故障切换(Failover)和健康检査(HealthChecking)功能—判断LVS 负载调度器、节点服务器的可用性当 master 主机出现故障及时切换到backup 节点保证业务正常…

LeetCode-刷题记录-滑动窗口合集(本篇blog会持续更新哦~)

一、滑动窗口概述 滑动窗口&#xff08;Sliding Window&#xff09;是一种用于解决数组&#xff08;或字符串&#xff09;中子数组&#xff08;或子串&#xff09;问题的有效算法。 Sliding Window核心思想&#xff1a; 滑动窗口技术的基本思想是维护一个窗口&#xff08;一般…

odoo 物联网 设备数据采集方案

图一 架构手稿(许老师专属) 图二 架构简图 部署 方案一&#xff1a; odoo业务数据库与设备采集数据库使用一个instance。 缺点&#xff1a;重启pg服务相互影响。 方案二&#xff1a; odoo业务数据库与设备采集数据库独立部署&#xff0c;使用两个instance。 优点&#xff1a;…

简单的git pull fail Can‘t update has no tracked branch解决记录

简单的git pull fail Can‘t update has no tracked branch解决记录 1. 问题描述 上午同事使用idea拉取代码的时候&#xff0c;发现拉取不了&#xff0c;提示用户权限问题&#xff0c;之后修改了git用户信息&#xff0c;发现还是拉取不了分支代码&#xff0c;然后删除了git r…

Java基础:爬虫

1.本地爬虫 Pattern:表示正则表达式 Matcher:文本匹配器&#xff0c;作用按照正则表达式的规则去读取字符串&#xff0c;从头开始读取。在大串中去找符合匹配规则的子串。 1.2.获取Pattern对象 通过Pattern p Pattern.compile("正则表达式");获得 1.3.…

Spire.PDF for .NET【文档操作】演示:以特定的缩放比例/百分比打开 PDF 文件

有时&#xff0c;我们可能需要在显示 PDF 文件时更改缩放比例以满足我们的要求。在本文中&#xff0c;我们将演示如何使用 Spire.PDF for .NET 以特定的缩放比例/百分比&#xff08;例如默认值、100% 或任何其他所需的缩放比例&#xff09;打开 PDF 文件。 Spire.PDF for .NET…

一种频偏估计与补偿方法

一种简易的频偏估计补偿方法&#xff0c;使用QAM等信号。估计精度受FFT长度限制&#xff0c;可以作为粗频偏估计。 Nfft 1024; % FFT长度 N 10*Nfft; % 仿真符号数 M 16; % 调制QAM16 freq 1e…

【SpringBoot3学习 | 第2篇】SpringBoot3整合+SpringBoot3项目打包运行

文章目录 一. SpringBoot3 整合 SpringMVC1.1 配置静态资源位置1.2 自定义拦截器&#xff08;SpringMVC配置&#xff09; 二. SpringBoot3 整合 Druid 数据源三. SpringBoot3 整合 Mybatis3.1 Mybatis整合3.2 声明式事务整合配置3.3 AOP整合配置 四. SpringBoot3 项目打包和运行…

三万字带你一遍跑通uer

三万字带你一遍跑通uer 参考文档 今天给大家介绍个非常强大的项目uer&#xff0c;集成了许多可以做自然语言的东西&#xff0c;效果的话也非常好&#xff0c;很适合企业级的应用&#xff01; 1. 先将项目uer从github拉取下来&#xff08;zip或git都ok&#xff09; 2. 用pycha…

Qt项目:基于Qt实现的网络聊天室---注册模块

文章目录 基本页面设计创建登录界面创建注册界面优化样式完善注册类界面 客户端逻辑完善客户端增加post逻辑客户端配置管理 邮箱注册服务认证服务读取配置邮箱验证服务联调设置验证码过期封装redis操作类封装redis连接池注册功能Server端接受注册请求封装mysql连接池封装DAO操作…