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

1、DispatcherServlet

java 复制代码
package com.csdn.mymvc.core;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.Test;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
@WebServlet("/*")
public class DispatcherServlet extends HttpServlet {

    private final String BEAN_FACTORY = "beanFactory";
    private final String CONTROLLER_BEAN_MAP = "controllerBeanMap";

    @Test
    public void uri() {
        String uri = "/fruit/index";
        String[] arr = uri.split("/");
        System.out.println(Arrays.toString(arr));//[, fruit, index]
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String[] staticResourceSuffixes = {".html", ".jsp", ".jpg", ".png", ".gif", ".css", ".js", ".ico"};
        String uri = req.getRequestURI();
        if (Arrays.stream(staticResourceSuffixes).anyMatch(uri::endsWith)) {
            RequestDispatcher defaultDispatcher = req.getServletContext().getNamedDispatcher("default");
            defaultDispatcher.forward(req, resp);
        } else {

            String[] arr = uri.split("/");

            if (arr == null || arr.length != 3) {
                throw new RuntimeException(uri + "非法!");
            }

            //[, fruit, index]
            String requestMapping = "/" + arr[1];
            String methodMapping = "/" + arr[2];

            ServletContext application = getServletContext();
            ControllerDefinition controllerDefinition = ((Map<String, ControllerDefinition>) application.getAttribute(CONTROLLER_BEAN_MAP)).get(requestMapping);

            if (controllerDefinition == null) {
                throw new RuntimeException(requestMapping + "对应的controller组件不存在!");
            }
            //获取请求方式,例如:get或者post
            String requestMethodStr = req.getMethod().toLowerCase();
            //get_/index
            Method method = controllerDefinition.getMethodMappingMap().get(requestMethodStr + "_" + methodMapping);
            Object controllerBean = controllerDefinition.getControllerBean();

            try {
                //调用controllerBean对象中的method方法
                method.setAccessible(true);
                method.invoke(controllerBean, req, resp);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }

        }
    }
}

2、ControllerDefinition

java 复制代码
package com.csdn.mymvc.core;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
//假设有一个uri是:/fruit/index
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ControllerDefinition {
    private String requestMapping;
    private Object controllerBean;
    private Map<String, Method> methodMappingMap = new HashMap<>();
}

3、ComponentScan

java 复制代码
package com.csdn.mymvc.core;
import com.csdn.mymvc.annotation.*;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.*;
public class ComponentScan {
    public static Map<String, Object> beanFactory = new HashMap<>();
    public static Map<String, ControllerDefinition> controllerBeanMap = new HashMap<>();
    static String path = null;
    static {
        //分析文件夹
        path = ComponentScan.class.getClassLoader().getResource("").getPath();
        //  /F:/IdeaProjects/workspace/review/pro13-fruit-DispatcherServlet/target/
        //  pro13-fruit-DispatcherServlet-1.0-SNAPSHOT/WEB-INF/classes/
        //计算机的硬盘根目录是 / ,不论是什么操作系统。只是微软人为的分出盘符的概念
        //System.out.println(path);
        path = path.substring(1);
        //System.out.println(path);
        //   F:/IdeaProjects/workspace/review/pro13-fruit-DispatcherServlet/target
        //   /pro13-fruit-DispatcherServlet-1.0-SNAPSHOT/WEB-INF/classes/
        File rootDir = new File(path);
        //开始解析文件夹 - 组件扫描工作开始
        try {
            //第 1 步:扫描类路径,解析出所有的bean实例,存放到IOC容器中(beanFactory)
            parseFile(rootDir);
            beanFactory.values().forEach(System.out::println);
            //第 2 步:经过第 1 步,所有的bean实例已经创建就绪,但是bean和bean之间的依赖关系没有注入(Injection)
            //本步骤实现 注入依赖关系
            beanFactory.values().forEach(bean -> {
                //获取bean内部所有的field
                Field[] fields = bean.getClass().getDeclaredFields();
                //获取每一个field上的注解信息
                Arrays.stream(fields)
                        .filter(field -> field.getDeclaredAnnotation(Autowire.class) != null)
                        .forEach(field -> {
                            //获取这个字段的类型的名称
                            String fieldTypeName = field.getType().getName();
                            //System.out.println(fieldTypeName);
                            Object filedValue = beanFactory.values().stream().filter(instance -> {
                                return field.getType().isAssignableFrom(instance.getClass());
                            }).findFirst().orElseThrow(() -> new RuntimeException(fieldTypeName + "装配失败!"));

                            try {
                                field.setAccessible(true);
                                field.set(bean, filedValue);
                            } catch (IllegalAccessException e) {
                                throw new RuntimeException(e);
                            }
                        });
            });

            //第 3 步:经过前两个步骤:IOC容器中已经准备好了所有的bean实例。并且bean实例之间的依赖关系也注入完成
            //这一步需要实现的是:uri是:/fruit/index    我们需要实现的是将uri中的两个标识分别映射到具体的controller实例以及controller方法上去
            //简单讲,这一步需要完成将每一个Controller都要存放到controllerBeanMap中
            beanFactory.values().stream()
                    .filter(bean -> bean.getClass().getDeclaredAnnotation(RequestMapping.class) != null)
                    .forEach(bean->{

                        ControllerDefinition controllerDefinition = new ControllerDefinition();

                        String requestMapping = bean.getClass().getDeclaredAnnotation(RequestMapping.class).value();
                        Object controllerBean = bean;

                        controllerDefinition.setRequestMapping(requestMapping);
                        controllerDefinition.setControllerBean(controllerBean);
                        //开始分析bean中的每一个方法
                        Arrays.stream(bean.getClass().getDeclaredMethods()).forEach(method -> {

                            GetMapping getMappingAnnotation = method.getDeclaredAnnotation(GetMapping.class);
                            String methodMapping = null;
                            if (getMappingAnnotation != null) {
                                methodMapping = getMappingAnnotation.value();
                                methodMapping = "get_" + methodMapping;
                            }
                            PostMapping postMappingAnnotation = method.getDeclaredAnnotation(PostMapping.class);
                            if (postMappingAnnotation != null) {
                                methodMapping = postMappingAnnotation.value();
                                methodMapping = "post_" + methodMapping;
                            }
                            if (methodMapping != null) {
                                controllerDefinition.getMethodMappingMap().put(methodMapping, method);
                            }
                        });
                        //将这个controllerDefinition存放到专门的Controller容器中
                        controllerBeanMap.put(requestMapping, controllerDefinition);
                    });
            System.out.println(beanFactory);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
    private static void parseFile(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        if (file.exists()) {
            if (file.isDirectory()) {
                //获取所有的子目录
                File[] childFiles = file.listFiles();
                for (File childFile : childFiles) {
                      parseFile(childFile);
                }
            } else {
                String absPath = file.getAbsolutePath();
                //System.out.println(absPath);
                String fullClassPath = absPath.substring(path.length());
                //System.out.println(fullClassPath);
                if (fullClassPath.endsWith(".class")) {
                    String fullClassPathName = fullClassPath.substring(0, fullClassPath.length() - ".class".length());
                    //System.out.println(fullClassPathName);
                    String fullClassName = fullClassPathName.replaceAll("\\\\", ".");
                    //System.out.println(fullClassName);
                    Class<?> clazz = Class.forName(fullClassName);
                    //System.out.println(clazz.toString());
                    if (clazz.toString().startsWith("class")) {  //排除掉接口、注解....,只关心class

                        if (!Modifier.isAbstract(clazz.getModifiers())) {   //排除掉抽象类

                            Optional<Annotation> optional = Arrays.stream(clazz.getDeclaredAnnotations()).filter(annotation -> {
                                return (annotation instanceof Controller || annotation instanceof Service || annotation instanceof Repository);
                            }).findFirst();

                            if (!optional.isEmpty()) {
                                Object bean = clazz.getDeclaredConstructor().newInstance();
                                beanFactory.put(fullClassName, bean);
                            }
                        }
                    }
                }
            }
        }
    }
}

4、ContextLoaderListener

java 复制代码
package com.csdn.mymvc.listener;
import com.csdn.mymvc.core.ComponentScan;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
@WebListener
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            Class.forName("com.csdn.mymvc.core.ComponentScan");
            ServletContext application = sce.getServletContext();
            application.setAttribute("beanFactory", ComponentScan.beanFactory);
            application.setAttribute("controllerBeanMap", ComponentScan.controllerBeanMap);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

5、FruitController

java 复制代码
package com.csdn.fruit.controller;
import com.csdn.fruit.dto.PageInfo;
import com.csdn.fruit.dto.PageQueryParam;
import com.csdn.fruit.dto.Result;
import com.csdn.fruit.pojo.Fruit;
import com.csdn.fruit.service.FruitService;
import com.csdn.fruit.util.RequestUtil;
import com.csdn.fruit.util.ResponseUtil;
import com.csdn.mymvc.annotation.*;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
@RequestMapping("/fruit")
public class FruitController {
    @Autowire
    private FruitService fruitService;

    @GetMapping("/index")
    protected void index(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Integer pageNo = 1;
        String pageNoStr = req.getParameter("pageNo");
        if (pageNoStr != null && !"".equals(pageNoStr)) {
            pageNo = Integer.parseInt(pageNoStr);
        }

        String keyword = "";
        String keywordStr = req.getParameter("keyword");
        if (keywordStr != null) {
            keyword = keywordStr;
        }
        PageQueryParam pageQueryParam = new PageQueryParam(pageNo, 5, keyword);
        PageInfo<Fruit> pageInfo = fruitService.getFruitPageInfo(pageQueryParam);

        Result result = Result.OK(pageInfo);

        ResponseUtil.print(resp, result);
    }

    @PostMapping("/add")
    protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Fruit fruit = (Fruit) RequestUtil.readObject(req, Fruit.class);
        fruitService.addFruit(fruit);
        ResponseUtil.print(resp, Result.OK());
    }

    @GetMapping("/del")
    protected void del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Integer fid = Integer.parseInt(req.getParameter("fid"));
        fruitService.delFruit(fid);
        ResponseUtil.print(resp, Result.OK());
    }

    @GetMapping("/edit")
    protected void edit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Integer fid = Integer.parseInt(req.getParameter("fid"));
        Fruit fruit = fruitService.getFruitById(fid);
        ResponseUtil.print(resp, Result.OK(fruit));
    }

    @GetMapping("/getFname")
    public void getFname(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        String fname = req.getParameter("fname");
        Fruit fruit = fruitService.getFruitByFname(fname);
        ResponseUtil.print(resp, fruit == null ? Result.OK() : Result.Fail());
    }

    @PostMapping("/update")
    protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Fruit fruit = (Fruit) RequestUtil.readObject(req, Fruit.class);
        fruitService.updateFruit(fruit);
        ResponseUtil.print(resp, Result.OK());
    }
}

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

6.1、index.js

javascript 复制代码
let pageNo = 1;
let pageCount = 0;
let keyword=""

//当页面加载完成,执行匿名函数
window.onload=function(){
    loadData();
}
function search() {
    keyword=$("#keyword").value
    pageNo = 1;
    loadData(pageNo)
}

function page(str) {
    if (str) {
        if (str == "first") {
            pageNo = 1;
        }else if (str == "pre") {
            pageNo = pageNo - 1;
        }else if (str == "next") {
            pageNo = pageNo + 1;
        }else if (str == "last") {
            pageNo = pageCount;
        }
        if (pageNo > pageCount) {
            pageNo=pageCount
        }
        if (pageNo <= 0) {
            pageNo=1
        }
    }
    loadData(pageNo)
}
loadData=function(pageNo=1){//pageNo这个参数有默认值,如果没有传值,则使用默认值 1
    axios({
        method: 'get',
        url: '/fruit/index',
        params: {
            pageNo: pageNo,
            keyword:keyword
        }
    }).then(response => {
        debugger
        let fruitList = response.data.data.list
        pageNo = response.data.data.pageNo
        pageCount = response.data.data.pageCount
        // 此处使用的是axios,那么响应回来的数据自动就是json,
        // 不需要再进行parse(如果是原始的ajax操作,那么一定需要parse)
        // let fruitArr = JSON.parse(fruitList)
        let fruitArr = fruitList;
        let fruitTbl = $("#fruit_tbl")
        //向表格中添加行之前,先删除原来的行
        let rows=fruitTbl.rows
        for (let i = rows.length - 1; i >= 1; i--) {
            fruitTbl.deleteRow(i);
        }

        for (let i = 0; i < fruitArr.length; i++) {
            let tr = fruitTbl.insertRow();
            let fnameTD = tr.insertCell();
            let priceTD = tr.insertCell();
            let fcountTD = tr.insertCell();
            let operTD = tr.insertCell();

            let fruit = fruitArr[i];
            //fnameTD.innerText = fruit.fname
            fnameTD.innerHTML = '<a href="edit.html?fid=' + fruit.fid + '">' + fruit.fname + '</a>';
            priceTD.innerText = fruit.price;
            fcountTD.innerText = fruit.fcount;
            operTD.innerHTML = "<img class=\"delImg\" src=\"imgs/del.png\" onclick=\"delFruit(" + fruit.fid + ")\"/>";
        }
    });
}

delFruit = function (fid) {
    if (window.confirm('是否确认删除?')) {
        axios({
            method: 'get',
            url: '/fruit/del',
            params:{
                fid: fid,
            }
        }).then(response=>{
            if (response.data.flag) {
                window.location.reload();
            }
        });
    }
};
相关推荐
suweijie7682 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿3 小时前
List深拷贝后,数据还是被串改
java
xlsw_6 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹7 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭7 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫8 小时前
泛型(2)
java
超爱吃士力架8 小时前
邀请逻辑
java·linux·后端
南宫生8 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石8 小时前
12/21java基础
java
李小白668 小时前
Spring MVC(上)
java·spring·mvc