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

手写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);
}

相关文章:

  • Web3 ETF的主要功能
  • 开源205W桌面充电器,140W+65W升降压PD3.1快充模块(2C+1A口),IP6557+IP6538
  • docker pull 镜像的时候遇到Pulling fs layer问题
  • Eigen 欧拉角转旋转矩阵相互转换
  • SpringBoot整合Dubbo的快速使用教程
  • 嵌入式MCU平台汇总
  • 视频编解码从H.264到H.266:浅析GB28181安防视频汇聚EasyCVR视频压缩技术
  • 解锁高效软件测试:虚拟机助力提升测试流程的秘诀
  • class类和style内联样式的绑定
  • 12款超良心好用APP推荐,每一款都值得下载!
  • odoo 物联网 设备数据采集方案
  • Qt项目:基于Qt实现的网络聊天室---注册模块
  • 1023记录
  • 数据结构——数组
  • 菜鸡的原地踏步史02(◐‿◑)
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • express如何解决request entity too large问题
  • oschina
  • Otto开发初探——微服务依赖管理新利器
  • PAT A1120
  • Python3爬取英雄联盟英雄皮肤大图
  • React-生命周期杂记
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 从伪并行的 Python 多线程说起
  • 大型网站性能监测、分析与优化常见问题QA
  • 老板让我十分钟上手nx-admin
  • 前端技术周刊 2019-01-14:客户端存储
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 小程序测试方案初探
  • 一道面试题引发的“血案”
  • python最赚钱的4个方向,你最心动的是哪个?
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • 容器镜像
  • 整理一些计算机基础知识!
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • 组复制官方翻译九、Group Replication Technical Details
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • #前后端分离# 头条发布系统
  • (10)ATF MMU转换表
  • (C++17) std算法之执行策略 execution
  • (javascript)再说document.body.scrollTop的使用问题
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)ssm高校实验室 毕业设计 800008
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)kafka实战——kafka源码编译启动
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (转) Android中ViewStub组件使用
  • (转)项目管理杂谈-我所期望的新人
  • (转载)(官方)UE4--图像编程----着色器开发
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)