• 项目实战:中央控制器实现(1)-基本功能实现-调用Controller中的方法


    1、DispatcherServlet 

    1. package com.csdn.mymvc.core;
    2. import jakarta.servlet.RequestDispatcher;
    3. import jakarta.servlet.ServletContext;
    4. import jakarta.servlet.ServletException;
    5. import jakarta.servlet.annotation.WebServlet;
    6. import jakarta.servlet.http.HttpServlet;
    7. import jakarta.servlet.http.HttpServletRequest;
    8. import jakarta.servlet.http.HttpServletResponse;
    9. import org.junit.Test;
    10. import java.io.IOException;
    11. import java.lang.reflect.InvocationTargetException;
    12. import java.lang.reflect.Method;
    13. import java.util.Arrays;
    14. import java.util.Map;
    15. @WebServlet("/*")
    16. public class DispatcherServlet extends HttpServlet {
    17. private final String BEAN_FACTORY = "beanFactory";
    18. private final String CONTROLLER_BEAN_MAP = "controllerBeanMap";
    19. @Test
    20. public void uri() {
    21. String uri = "/fruit/index";
    22. String[] arr = uri.split("/");
    23. System.out.println(Arrays.toString(arr));//[, fruit, index]
    24. }
    25. @Override
    26. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    27. String[] staticResourceSuffixes = {".html", ".jsp", ".jpg", ".png", ".gif", ".css", ".js", ".ico"};
    28. String uri = req.getRequestURI();
    29. if (Arrays.stream(staticResourceSuffixes).anyMatch(uri::endsWith)) {
    30. RequestDispatcher defaultDispatcher = req.getServletContext().getNamedDispatcher("default");
    31. defaultDispatcher.forward(req, resp);
    32. } else {
    33. String[] arr = uri.split("/");
    34. if (arr == null || arr.length != 3) {
    35. throw new RuntimeException(uri + "非法!");
    36. }
    37. //[, fruit, index]
    38. String requestMapping = "/" + arr[1];
    39. String methodMapping = "/" + arr[2];
    40. ServletContext application = getServletContext();
    41. ControllerDefinition controllerDefinition = ((Map) application.getAttribute(CONTROLLER_BEAN_MAP)).get(requestMapping);
    42. if (controllerDefinition == null) {
    43. throw new RuntimeException(requestMapping + "对应的controller组件不存在!");
    44. }
    45. //获取请求方式,例如:get或者post
    46. String requestMethodStr = req.getMethod().toLowerCase();
    47. //get_/index
    48. Method method = controllerDefinition.getMethodMappingMap().get(requestMethodStr + "_" + methodMapping);
    49. Object controllerBean = controllerDefinition.getControllerBean();
    50. try {
    51. //调用controllerBean对象中的method方法
    52. method.setAccessible(true);
    53. method.invoke(controllerBean, req, resp);
    54. } catch (IllegalAccessException e) {
    55. e.printStackTrace();
    56. throw new RuntimeException(e);
    57. } catch (InvocationTargetException e) {
    58. e.printStackTrace();
    59. throw new RuntimeException(e);
    60. }
    61. }
    62. }
    63. }

    2、ControllerDefinition 

    1. package com.csdn.mymvc.core;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. import java.lang.reflect.Method;
    6. import java.util.HashMap;
    7. import java.util.Map;
    8. //假设有一个uri是:/fruit/index
    9. @Data
    10. @NoArgsConstructor
    11. @AllArgsConstructor
    12. public class ControllerDefinition {
    13. private String requestMapping;
    14. private Object controllerBean;
    15. private Map methodMappingMap = new HashMap<>();
    16. }

    3、ComponentScan

    1. package com.csdn.mymvc.core;
    2. import com.csdn.mymvc.annotation.*;
    3. import java.io.File;
    4. import java.lang.annotation.Annotation;
    5. import java.lang.reflect.Field;
    6. import java.lang.reflect.InvocationTargetException;
    7. import java.lang.reflect.Modifier;
    8. import java.util.*;
    9. public class ComponentScan {
    10. public static Map beanFactory = new HashMap<>();
    11. public static Map controllerBeanMap = new HashMap<>();
    12. static String path = null;
    13. static {
    14. //分析文件夹
    15. path = ComponentScan.class.getClassLoader().getResource("").getPath();
    16. // /F:/IdeaProjects/workspace/review/pro13-fruit-DispatcherServlet/target/
    17. // pro13-fruit-DispatcherServlet-1.0-SNAPSHOT/WEB-INF/classes/
    18. //计算机的硬盘根目录是 / ,不论是什么操作系统。只是微软人为的分出盘符的概念
    19. //System.out.println(path);
    20. path = path.substring(1);
    21. //System.out.println(path);
    22. // F:/IdeaProjects/workspace/review/pro13-fruit-DispatcherServlet/target
    23. // /pro13-fruit-DispatcherServlet-1.0-SNAPSHOT/WEB-INF/classes/
    24. File rootDir = new File(path);
    25. //开始解析文件夹 - 组件扫描工作开始
    26. try {
    27. //第 1 步:扫描类路径,解析出所有的bean实例,存放到IOC容器中(beanFactory)
    28. parseFile(rootDir);
    29. beanFactory.values().forEach(System.out::println);
    30. //第 2 步:经过第 1 步,所有的bean实例已经创建就绪,但是bean和bean之间的依赖关系没有注入(Injection)
    31. //本步骤实现 注入依赖关系
    32. beanFactory.values().forEach(bean -> {
    33. //获取bean内部所有的field
    34. Field[] fields = bean.getClass().getDeclaredFields();
    35. //获取每一个field上的注解信息
    36. Arrays.stream(fields)
    37. .filter(field -> field.getDeclaredAnnotation(Autowire.class) != null)
    38. .forEach(field -> {
    39. //获取这个字段的类型的名称
    40. String fieldTypeName = field.getType().getName();
    41. //System.out.println(fieldTypeName);
    42. Object filedValue = beanFactory.values().stream().filter(instance -> {
    43. return field.getType().isAssignableFrom(instance.getClass());
    44. }).findFirst().orElseThrow(() -> new RuntimeException(fieldTypeName + "装配失败!"));
    45. try {
    46. field.setAccessible(true);
    47. field.set(bean, filedValue);
    48. } catch (IllegalAccessException e) {
    49. throw new RuntimeException(e);
    50. }
    51. });
    52. });
    53. //第 3 步:经过前两个步骤:IOC容器中已经准备好了所有的bean实例。并且bean实例之间的依赖关系也注入完成
    54. //这一步需要实现的是:uri是:/fruit/index 我们需要实现的是将uri中的两个标识分别映射到具体的controller实例以及controller方法上去
    55. //简单讲,这一步需要完成将每一个Controller都要存放到controllerBeanMap中
    56. beanFactory.values().stream()
    57. .filter(bean -> bean.getClass().getDeclaredAnnotation(RequestMapping.class) != null)
    58. .forEach(bean->{
    59. ControllerDefinition controllerDefinition = new ControllerDefinition();
    60. String requestMapping = bean.getClass().getDeclaredAnnotation(RequestMapping.class).value();
    61. Object controllerBean = bean;
    62. controllerDefinition.setRequestMapping(requestMapping);
    63. controllerDefinition.setControllerBean(controllerBean);
    64. //开始分析bean中的每一个方法
    65. Arrays.stream(bean.getClass().getDeclaredMethods()).forEach(method -> {
    66. GetMapping getMappingAnnotation = method.getDeclaredAnnotation(GetMapping.class);
    67. String methodMapping = null;
    68. if (getMappingAnnotation != null) {
    69. methodMapping = getMappingAnnotation.value();
    70. methodMapping = "get_" + methodMapping;
    71. }
    72. PostMapping postMappingAnnotation = method.getDeclaredAnnotation(PostMapping.class);
    73. if (postMappingAnnotation != null) {
    74. methodMapping = postMappingAnnotation.value();
    75. methodMapping = "post_" + methodMapping;
    76. }
    77. if (methodMapping != null) {
    78. controllerDefinition.getMethodMappingMap().put(methodMapping, method);
    79. }
    80. });
    81. //将这个controllerDefinition存放到专门的Controller容器中
    82. controllerBeanMap.put(requestMapping, controllerDefinition);
    83. });
    84. System.out.println(beanFactory);
    85. } catch (ClassNotFoundException e) {
    86. throw new RuntimeException(e);
    87. } catch (InvocationTargetException e) {
    88. throw new RuntimeException(e);
    89. } catch (NoSuchMethodException e) {
    90. throw new RuntimeException(e);
    91. } catch (InstantiationException e) {
    92. throw new RuntimeException(e);
    93. } catch (IllegalAccessException e) {
    94. throw new RuntimeException(e);
    95. }
    96. }
    97. private static void parseFile(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    98. if (file.exists()) {
    99. if (file.isDirectory()) {
    100. //获取所有的子目录
    101. File[] childFiles = file.listFiles();
    102. for (File childFile : childFiles) {
    103. parseFile(childFile);
    104. }
    105. } else {
    106. String absPath = file.getAbsolutePath();
    107. //System.out.println(absPath);
    108. String fullClassPath = absPath.substring(path.length());
    109. //System.out.println(fullClassPath);
    110. if (fullClassPath.endsWith(".class")) {
    111. String fullClassPathName = fullClassPath.substring(0, fullClassPath.length() - ".class".length());
    112. //System.out.println(fullClassPathName);
    113. String fullClassName = fullClassPathName.replaceAll("\\\\", ".");
    114. //System.out.println(fullClassName);
    115. Class clazz = Class.forName(fullClassName);
    116. //System.out.println(clazz.toString());
    117. if (clazz.toString().startsWith("class")) { //排除掉接口、注解....,只关心class
    118. if (!Modifier.isAbstract(clazz.getModifiers())) { //排除掉抽象类
    119. Optional optional = Arrays.stream(clazz.getDeclaredAnnotations()).filter(annotation -> {
    120. return (annotation instanceof Controller || annotation instanceof Service || annotation instanceof Repository);
    121. }).findFirst();
    122. if (!optional.isEmpty()) {
    123. Object bean = clazz.getDeclaredConstructor().newInstance();
    124. beanFactory.put(fullClassName, bean);
    125. }
    126. }
    127. }
    128. }
    129. }
    130. }
    131. }
    132. }

    4、ContextLoaderListener 

    1. package com.csdn.mymvc.listener;
    2. import com.csdn.mymvc.core.ComponentScan;
    3. import jakarta.servlet.ServletContext;
    4. import jakarta.servlet.ServletContextEvent;
    5. import jakarta.servlet.ServletContextListener;
    6. import jakarta.servlet.annotation.WebListener;
    7. @WebListener
    8. public class ContextLoaderListener implements ServletContextListener {
    9. @Override
    10. public void contextInitialized(ServletContextEvent sce) {
    11. try {
    12. Class.forName("com.csdn.mymvc.core.ComponentScan");
    13. ServletContext application = sce.getServletContext();
    14. application.setAttribute("beanFactory", ComponentScan.beanFactory);
    15. application.setAttribute("controllerBeanMap", ComponentScan.controllerBeanMap);
    16. } catch (ClassNotFoundException e) {
    17. throw new RuntimeException(e);
    18. }
    19. }
    20. }

     5、FruitController

    1. package com.csdn.fruit.controller;
    2. import com.csdn.fruit.dto.PageInfo;
    3. import com.csdn.fruit.dto.PageQueryParam;
    4. import com.csdn.fruit.dto.Result;
    5. import com.csdn.fruit.pojo.Fruit;
    6. import com.csdn.fruit.service.FruitService;
    7. import com.csdn.fruit.util.RequestUtil;
    8. import com.csdn.fruit.util.ResponseUtil;
    9. import com.csdn.mymvc.annotation.*;
    10. import jakarta.servlet.ServletException;
    11. import jakarta.servlet.http.HttpServletRequest;
    12. import jakarta.servlet.http.HttpServletResponse;
    13. import java.io.IOException;
    14. @Controller
    15. @RequestMapping("/fruit")
    16. public class FruitController {
    17. @Autowire
    18. private FruitService fruitService;
    19. @GetMapping("/index")
    20. protected void index(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    21. Integer pageNo = 1;
    22. String pageNoStr = req.getParameter("pageNo");
    23. if (pageNoStr != null && !"".equals(pageNoStr)) {
    24. pageNo = Integer.parseInt(pageNoStr);
    25. }
    26. String keyword = "";
    27. String keywordStr = req.getParameter("keyword");
    28. if (keywordStr != null) {
    29. keyword = keywordStr;
    30. }
    31. PageQueryParam pageQueryParam = new PageQueryParam(pageNo, 5, keyword);
    32. PageInfo pageInfo = fruitService.getFruitPageInfo(pageQueryParam);
    33. Result result = Result.OK(pageInfo);
    34. ResponseUtil.print(resp, result);
    35. }
    36. @PostMapping("/add")
    37. protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    38. Fruit fruit = (Fruit) RequestUtil.readObject(req, Fruit.class);
    39. fruitService.addFruit(fruit);
    40. ResponseUtil.print(resp, Result.OK());
    41. }
    42. @GetMapping("/del")
    43. protected void del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    44. Integer fid = Integer.parseInt(req.getParameter("fid"));
    45. fruitService.delFruit(fid);
    46. ResponseUtil.print(resp, Result.OK());
    47. }
    48. @GetMapping("/edit")
    49. protected void edit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    50. Integer fid = Integer.parseInt(req.getParameter("fid"));
    51. Fruit fruit = fruitService.getFruitById(fid);
    52. ResponseUtil.print(resp, Result.OK(fruit));
    53. }
    54. @GetMapping("/getFname")
    55. public void getFname(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    56. String fname = req.getParameter("fname");
    57. Fruit fruit = fruitService.getFruitByFname(fname);
    58. ResponseUtil.print(resp, fruit == null ? Result.OK() : Result.Fail());
    59. }
    60. @PostMapping("/update")
    61. protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    62. Fruit fruit = (Fruit) RequestUtil.readObject(req, Fruit.class);
    63. fruitService.updateFruit(fruit);
    64. ResponseUtil.print(resp, Result.OK());
    65. }
    66. }

    6、有一个小bug,查询的时候需要重置为首页

    6.1、index.js

    1. let pageNo = 1;
    2. let pageCount = 0;
    3. let keyword=""
    4. //当页面加载完成,执行匿名函数
    5. window.onload=function(){
    6. loadData();
    7. }
    8. function search() {
    9. keyword=$("#keyword").value
    10. pageNo = 1;
    11. loadData(pageNo)
    12. }
    13. function page(str) {
    14. if (str) {
    15. if (str == "first") {
    16. pageNo = 1;
    17. }else if (str == "pre") {
    18. pageNo = pageNo - 1;
    19. }else if (str == "next") {
    20. pageNo = pageNo + 1;
    21. }else if (str == "last") {
    22. pageNo = pageCount;
    23. }
    24. if (pageNo > pageCount) {
    25. pageNo=pageCount
    26. }
    27. if (pageNo <= 0) {
    28. pageNo=1
    29. }
    30. }
    31. loadData(pageNo)
    32. }
    33. loadData=function(pageNo=1){//pageNo这个参数有默认值,如果没有传值,则使用默认值 1
    34. axios({
    35. method: 'get',
    36. url: '/fruit/index',
    37. params: {
    38. pageNo: pageNo,
    39. keyword:keyword
    40. }
    41. }).then(response => {
    42. debugger
    43. let fruitList = response.data.data.list
    44. pageNo = response.data.data.pageNo
    45. pageCount = response.data.data.pageCount
    46. // 此处使用的是axios,那么响应回来的数据自动就是json,
    47. // 不需要再进行parse(如果是原始的ajax操作,那么一定需要parse)
    48. // let fruitArr = JSON.parse(fruitList)
    49. let fruitArr = fruitList;
    50. let fruitTbl = $("#fruit_tbl")
    51. //向表格中添加行之前,先删除原来的行
    52. let rows=fruitTbl.rows
    53. for (let i = rows.length - 1; i >= 1; i--) {
    54. fruitTbl.deleteRow(i);
    55. }
    56. for (let i = 0; i < fruitArr.length; i++) {
    57. let tr = fruitTbl.insertRow();
    58. let fnameTD = tr.insertCell();
    59. let priceTD = tr.insertCell();
    60. let fcountTD = tr.insertCell();
    61. let operTD = tr.insertCell();
    62. let fruit = fruitArr[i];
    63. //fnameTD.innerText = fruit.fname
    64. fnameTD.innerHTML = 'fid + '">' + fruit.fname + '';
    65. priceTD.innerText = fruit.price;
    66. fcountTD.innerText = fruit.fcount;
    67. operTD.innerHTML = " + fruit.fid + ")\"/>";
    68. }
    69. });
    70. }
    71. delFruit = function (fid) {
    72. if (window.confirm('是否确认删除?')) {
    73. axios({
    74. method: 'get',
    75. url: '/fruit/del',
    76. params:{
    77. fid: fid,
    78. }
    79. }).then(response=>{
    80. if (response.data.flag) {
    81. window.location.reload();
    82. }
    83. });
    84. }
    85. };

  • 相关阅读:
    字符串——算法专项刷题(三)
    利用Docker 实现 MiniOB环境搭建
    JC/T 482-2022 聚氨酯建筑密封胶检测
    Access denied for user ‘root‘@‘localhost‘ (using password: YES)解决方案
    A Generic Deep-Learning-Based Approach for Automated Surface Inspection-论文阅读笔记
    人大金仓Windows下可以使用计划任务吗?创建完没有反应?
    【愚公系列】2022年10月 LiteDB数据库-.Net Core中的使用
    CoDeSys系列-4、基于Ubuntu的codesys运行时扩展包搭建Profinet主从环境
    vim模式用法总结
    【Android Studio学习】第二篇、APP实现画简易的波形图
  • 原文地址:https://blog.csdn.net/m0_65152767/article/details/134295764