手写SpringMVC之调度器DispatcherServlet

news/2024/7/8 7:45:30 标签: tomcat, java

DispatcherServlet:分发、调度

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

image-20240627203051007

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

java">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

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

第三步:调用对应的方法

java">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:将调用的方法的参数列表与请求数据适配
java">/**
 * 集中处理参数的函数,通过判断参数的类型,对不同类型的参数进行处理,包括
 * 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

java">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
    };
java">/**
 * 判断参数的类型是不是常见类型 [COMMON_CLASSES]
 *
 * @param type 参数类型
 * @return 是否为常见类型
 */
public boolean judgeTypeIsJavaOrNot(Class<?> type) {
    for (Class<?> clazz : COMMON_CLASSES) {
        if (type == clazz) {
            return true;
        }
    }
    return false;
}
handlerJavaType:处理常见参数
java">/**
 * 处理不同的常见数据类型,将其转化为对应的数据并添加到参数列表对应的位置
 *
 * @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参数
java">/**
 * 处理方法的参数是集合类型的方法,如果参数是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.User

    List<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:处理自定义参数
java">/**
 * 处理自定义类型
 *
 * @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 --> Object
            obj = 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请求体中
java">/**
 * 将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注解],就直接将数据发送到前端
java">/**
 * 处理返回值的函数,通过判断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:跳转页面
java">/**
 * 页面跳转函数
 *
 * @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:直接返回响应
java">/**
 * 向前端发送JSON数据
 * @param jsonStr JSON字符串
 * @param response 相应提对象
 */
public void sendResponse( String jsonStr,HttpServletResponse response) throws IOException {
    response.getWriter().write(jsonStr);
}

http://www.niftyadmin.cn/n/5536836.html

相关文章

Ubuntu20.04突然没网的一种解决办法

本来要学一下点云地图处理&#xff0c;用octomap库&#xff0c;但是提示少了octomap-server库&#xff0c;然后通过下面命令安装的时候&#xff1a; sudo apt install ros-noetic-octomap-server 提示&#xff1a;错误:7 https://mirrors.ustc.edu.cn/ubuntu focal-security …

电商数据仓库

1.数据仓库的数据来源为业务数据库&#xff08;mysql&#xff09; 2.通过sqoop将mysql中的业务数据导入到大数据平台&#xff08;hive&#xff09; 3.通过hive进行数据计算和数据分析 形成数据报表 4.再通过sqoop将数据报表导出到mysql 5.使用FineReport制作数据报表 1.数据…

Pytorch实战(二):VGG神经网络

文章目录 一、诞生背景二、VGG网络结构2.1VGG块2.2网络运行流程2.3总结 三、实战3.1搭建模型3.2模型训练3.3训练结果可视化3.4模型参数初始化 一、诞生背景 从网络结构中可看出&#xff0c;所有版本VGG均全部使用33大小、步长为1的小卷积核&#xff0c;33卷积核同时也是最小的能…

[Day 20] 區塊鏈與人工智能的聯動應用:理論、技術與實踐

AI在醫療領域的創新應用 隨著科技的快速發展&#xff0c;人工智能&#xff08;AI&#xff09;在各行各業的應用越來越廣泛&#xff0c;醫療領域也不例外。AI技術在醫療中的應用不僅提高了診斷的準確性&#xff0c;還改善了病患的治療效果&#xff0c;優化了醫療資源的配置。本…

Linux python3.6安装mayavi报错

需要将vtk版本降级&#xff0c;以及uninstall pyqt5&#xff08;安装的vtk版本是9.3.1&#xff09; pip3 install vtk8.1.0 或者9.0.1 报错 Building wheels for collected packages: mayavi Building wheel for mayavi (setup.py) ... error ERROR: Command errored out…

【笔记】字符串相似度代码分享

目录 一、算法介绍1、算法1&#xff09;基于编辑距离2&#xff09;基于标记3&#xff09;基于序列4&#xff09;基于压缩5&#xff09;基于发音6&#xff09;简单算法 2、安装 二、代码demo1、Hamming 距离2、Levenshtein 距离3、Damerau-Levenshtein距离4、Jaro 相似度5、Jaro…

图形的搭建

例一&#xff1a; 输入描述&#xff1a; 多组输入&#xff0c;一个整数&#xff08;2~20&#xff09;&#xff0c;表示输出的行数&#xff0c;也表示组成“X”的反斜线和正斜线的长度。 输出描述&#xff1a; 针对每行输入&#xff0c;输出用“*”组成的X形图案。 示例一&…

1_插入排序_循环不变式

01_插入排序 #include<stdio.h>void insert_sort(int arr[], int n); void printArray(int arr[], int size);int main() {int arr[] {1, 2, 3, 22, 5, 9};int n sizeof(arr) / sizeof(arr[0]);printf("打印原始数组:\n");prinfArray(arr, n);insert_sort(a…