对MVC的理解以及如何实现一个简单的MVC

IOC 容器与 Controller
  • 在 Spring 框架中,所有的 Controller 都会被 Spring 的 IOC 容器管理。
  • 当应用程序启动时,Spring 会扫描所有带有 @Controller 注解的类,并将它们作为 Bean 注册到 IOC 容器中。
方法扫描与 Dispatcher
  • Spring MVC 在启动时,会扫描所有 Controller 类中的方法。
  • 根据方法上的注解(例如 @RequestMapping, @GetMapping, @PostMapping 等),Spring MVC 将这些方法与特定的 URL 模式映射,并将这些映射添加到一个中央的 DispatcherServlet 中。
参数封装
  • 每个 Controller 方法的参数以及参数上的注解(如 @RequestParam, @PathVariable, @RequestBody 等)都会被封装成 Param 对象。
  • 这些 Param 对象包含了参数的类型、名称、默认值和注解类型等信息,并被存储在 methodParameters 列表中。
请求处理
  • 当一个 HTTP 请求到达时,DispatcherServlet 会根据请求的 URL 匹配到相应的处理器(Controller 方法)。
  • DispatcherServlet 调用匹配的处理器的 process 方法来处理请求。
  • process 方法中,根据请求的具体信息,调用对应的 Controller 方法。
返回结果
  • Controller 方法的返回值会被封装成一个 Result 对象,包含了处理是否成功以及处理的具体结果。
  • Result 对象会传递给视图解析器,将结果渲染成最终的视图(如 HTML、JSON 等),并返回给客户端。

实现一个简单的MVC

定义Dispatcher

    static class Dispatcher {

        final static Result NOT_PROCESSED = new Result(false, null);

        Logger logger = LoggerFactory.getLogger(getClass());

        boolean isRest;

        boolean isResponseBody;

        boolean isVoid;

        Pattern urlPattern;

        Object controller;

        Method handlerMethod;

        Param[] methodParameters;


        public Dispatcher(String httpMethod, boolean isRest, Object controller, Method method, String urlPattern) throws ServletException {
            this.isRest = isRest;
            this.isResponseBody = method.getAnnotation(ResponseBody.class) != null;
            this.isVoid = method.getReturnType() == void.class;
            this.urlPattern = Pattern.compile(urlPattern);
            this.controller = controller;
            this.handlerMethod = method;
            Parameter[] param = method.getParameters();
            Annotation[][] paramAnnos = method.getParameterAnnotations();
            this.methodParameters = new Param[param.length];

            for (int i = 0; i < param.length; i++) {
                methodParameters[i] = new Param(httpMethod, method, param[i], paramAnnos[i]);
            }
        }

定义Param

    static class Param {
        String name;

        ParamType paramType;

        Class<?> classType;

        String defaultValue;

        /**
         * 对参数进行解析,并设置参数类型、参数名、参数默认值
         *
         * @param httpMethod
         * @param method
         * @param parameter
         * @param annotations
         * @throws ServletException
         */
        public Param(String httpMethod, Method method, Parameter parameter, Annotation[] annotations) throws ServletException {
            PathVariable pv = ClassUtils.getAnnotaion(annotations, PathVariable.class);
            RequestParam rq = ClassUtils.getAnnotaion(annotations, RequestParam.class);
            RequestBody rb = ClassUtils.getAnnotaion(annotations, RequestBody.class);
            int total = (pv == null ? 0 : 1) + (rq == null ? 0 : 1) + (rb == null ? 0 : 1);
            if (total > 1) {
                throw new ServletException("Only one annotation can be used in a parameter." + method);
            }

            this.classType = parameter.getType();

            if (pv != null) {
                this.name = pv.value();
                this.paramType = ParamType.PATH_VARIABLE;
            } else if (rq != null) {
                this.name = rq.value();
                this.defaultValue = rq.defaultValue();
                this.paramType = ParamType.REQUEST_PARAM;
            } else if (rb != null) {
                this.paramType = ParamType.REQUEST_BODY;
            } else {
                this.paramType = ParamType.SERVLET_VARIABLE;
                if (this.classType != HttpServletRequest.class && this.classType != HttpServletResponse.class &&
                        this.classType != HttpSession.class && this.classType != ServletContext.class) {
                    throw new ServerErrorException("请给参数标记注解,不支持的参数类型 " + classType + " at method " + method);
                }
            }
        }

定义处理结果

    static record Result(boolean processed, Object returnObject) {
    }

初始化

   @Override
    public void init(ServletConfig config) throws ServletException {
        logger.info("init{}.", getClass().getName());
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) this.applicationContext;
        List<BeanDefinition> beanDefinitions = configurableApplicationContext.findBeanDefinitions(Object.class);
        for (BeanDefinition def : beanDefinitions) {
            Class<?> beanClass = def.getBeanClass();
            Object bean = def.getRequiredInstance();
            Controller controller = beanClass.getAnnotation(Controller.class);
            RestController restController = beanClass.getAnnotation(RestController.class);
            if (controller != null && restController != null) {
                throw new ServletException("Controller and RestController can not be used at the same time." + beanClass.getName());
            }
            if (controller != null) {
                addController(false, def.getName(), bean);
            }else{
                addController(true, def.getName(), bean);
            }
        }
    }

doService

    void doService(String url, HttpServletRequest req, HttpServletResponse resp, List<Dispatcher> dispatchers) throws Exception {
        for (Dispatcher dispatcher : dispatchers){
            Result result = dispatcher.process(url, req, resp);
            if(result.processed()){
                Object r = result.returnObject();
                if(dispatcher.isRest){
                    if(!resp.isCommitted()){
                        resp.setContentType("application/json;charset=UTF-8");
                    }
                    if(dispatcher.isResponseBody){
                        if(r instanceof String s){
                            PrintWriter pw = resp.getWriter();
                            pw.write(s);
                            pw.flush();
                        }else if(r instanceof byte[] data){
                            ServletOutputStream output = resp.getOutputStream();
                            output.write(data);
                            output.flush();
                        }else{
                            throw new ServerErrorException("Unsupported return type: " + r.getClass());
                        }
                    }
                }else{
                    if( !resp.isCommitted()){
                        resp.setContentType("text/html");
                    }
                    if( r instanceof String s){
                        if (dispatcher.isResponseBody) {
                            // send as response body:
                            PrintWriter pw = resp.getWriter();
                            pw.write(s);
                            pw.flush();
                        } else if (s.startsWith("redirect:")) {
                            // send redirect:
                            resp.sendRedirect(s.substring(9));
                        } else {
                            // error:
                            throw new ServletException("Unable to process String result when handle url: " + url);
                        }
                    } else if (r instanceof byte[] data) {
                        if (dispatcher.isResponseBody) {
                            // send as response body:
                            ServletOutputStream output = resp.getOutputStream();
                            output.write(data);
                            output.flush();
                        } else {
                            // error:
                            throw new ServletException("Unable to process byte[] result when handle url: " + url);
                        }
                    }else if(r instanceof ModelAndView mv){
                        String view = mv.getViewName();
                        if (view.startsWith("redirect:")) {
                            // send redirect:
                            resp.sendRedirect(view.substring(9));
                        } else {
                            this.viewResolver.render(view, mv.getModel(), req, resp);
                        }
                    } else if (!dispatcher.isVoid && r != null) {
                        // error:
                        throw new ServletException("Unable to process " + r.getClass().getName() + " result when handle url: " + url);
                    }
                }
            }
            return;
        }

    }

详细代码地址
https://github.com/laicoffee/Spring-Summer

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python番外篇之责任转移:有关于虚拟机编程语言的往事

编程之痛 如果&#xff0c;你像笔者一样&#xff0c;有过学习或者使用汇编语言与C、C等语言的经历&#xff0c;一定对下面所说的痛苦感同身受。 汇编语言 将以二进制表示的一条条CPU的机器指令&#xff0c;以人类可读的方式进行表示。虽然&#xff0c;人类可读了&#xff0c…

thinksboard新建table表格

html文件 <div fxFlex fxLayoutAlign"left top" style"display: block"> <!-- <mat-card appearance"raised" style"max-height: 80vh; overflow-y: auto;">--><div><button mat-raised-button (click)&…

数据结构(JAVA)—代码题

01-数据结构—判断题 02-数据结构—选择题 03 数据结构—多选填空程序填空 ​ 01-顺序表的建立及遍历 import java.util.Iterator; import java.util.LinkedList; import java.util.ListIterator; import java.util.Scanner;public class Main {public static void main(St…

告别熬夜改稿:AI降重工具让论文降重变得轻松又有趣

已经天临五年了&#xff0c;大学生们还在为论文降重烦恼……手动降重确实是个难题&#xff0c;必须要先付点小经费去靠谱的网站查重&#xff0c;再对着红字标注去改&#xff0c;后面每一次的论文呢查重结果都像赌//博&#xff0c;谁也不知道明明是同一篇文章&#xff0c;第二次…

【C语言】union 关键字

在C语言中&#xff0c;union关键字用于定义联合体。联合体是一种特殊的数据结构&#xff0c;它允许不同的数据类型共享同一段内存。所有联合体成员共享同一个内存位置&#xff0c;因此联合体的大小取决于其最大成员的大小。 定义和使用联合体 基本定义 定义一个联合体类型时…

【MySQL】MySQL锁冲突排障纪要

【MySQL】MySQL锁冲突排障纪要 开篇词&#xff1a;干货篇&#xff1a;1.查看当前innodb status,里面包含事务,锁占用情况2.查看mysql进程状态3.查看innodb事务&#xff0c;锁&#xff0c;锁等待情况4.定位持有锁的线程信息 总结篇&#xff1a;一、锁冲突的原因二、锁冲突的表现…

【Python】列表

目录 一、列表的概念 二、列表的创建 1.变量名 [ ] ..... 2.通过Python内置 的I ist类的构造函数来创建列表 三、操作列表元素的方法 1. 修改 2. 增加元素 3. 删除 4. 其他操作 四、遍历列表 五、列表排序 六、列表切片&#xff08;list slicing&#xff09; 七、…

Python入门 2024/7/2

目录 格式化的精度控制 字符串格式化 对表达式进行格式化 小练习&#xff08;股票计算小程序&#xff09; 数据输入 布尔类型和比较运算符 if语句 小练习&#xff08;成人判断&#xff09; if-else语句 if-elif-else语句 练习&#xff1a;猜猜心里数字 嵌套语句 猜…

JavaScript中的Array(数组)对象

目录 一、Array数组对象 1、介绍 2、创建数组对象并赋值 3、访问数组元素 二、Array对象属性 1、constructor属性 2、length属性 3、prototype属性 三、Array对象的常用方法 1、isArray() 2、concat() 3、pop() 4、shift() 5、push() 6、unshift() 7、reverse(…

前端进阶:Vue.js

目录 框架&#xff1a; 助解&#xff1a; 框架&#xff1a; VUE 什么是Vue.js? Vue.js优点 Vue安装 方式一&#xff1a;直接用<script>引入 方式二&#xff1a;命令行工具 第一个Vue程序 代码 代码解释&#xff1a; 运行 Vue指令 v-text v-html v-tex…

git 中有关 old mode 100644、new mode 10075的问题解决小结

问题&#xff1a; 同一个文件被修改后&#xff0c;最后代码没有变&#xff08;代码刚开始修改了&#xff0c;最后又删除还原了&#xff09;&#xff0c;文件变了&#xff0c;导致提交了一个空文件 git diff 提示 filemode 发生改变&#xff08;old mode 100644、new mode 1007…

RabbitMQ进阶篇

文章目录 发送者的可靠性生产者重试机制实现生产者确认 MQ的可靠性数据持久化交换机持久化队列持久化消息持久化 Lazy Queue(可配置~)控制台配置Lazy模式代码配置Lazy模式更新已有队列为lazy模式 消费者的可靠性消费者确认机制失败重试机制失败处理策略 业务幂等性唯一消息ID业…

layui-页面布局

1.布局容器 分为固定和完整宽度 class layui-container 是固定宽度 layui-fluid是完整宽度

傻瓜交换机多网段互通组网、设备无法配置网关案例

记录一下&#xff1a; 一、傻瓜交换机多网段互通组网 1、客户在核心交换机上创建了VLAN10&#xff0c;VLAN20。 VLAN10&#xff1a;IP192.168.10.254 VLAN20&#xff1a;IP192.168.20.254 在核心交换机下挂了一台傻瓜交换机&#xff0c;傻瓜交换机接入了一台OA服务器IP&#…

从零开始:在Windows上部署大型模型

这是一个超详细安装教程&#xff0c;介绍了在 Window 电脑上如何部署 Qwen1.5 大模型。本文还涉及到 Python 及其环境的配置。 适合对象&#xff1a;有点后端编程基础&#xff0c;没有 Python 基础。 需要环境&#xff1a;Window10/11&#xff0c;支持 Cuda 的 Nvidia 显卡。…

数据结构与算法-动态规划-最长回文子串

最长回文子串 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解释&#xff1a;"aba" 同样是符合题意的答案。示例 2&#xff1a; 输入&#xff1a;s "…

识图ACWP.BCWS.BCWP

将三个概念想象成三个角色&#xff08;如&#xff1a;勇士、法师、盗贼&#xff09;&#xff0c;其中&#xff1a; ACWP是勇士&#xff0c;代表实际力量&#xff08;实际成本&#xff09;&#xff1b;BCWS是法师&#xff0c;代表预期魔法&#xff08;预算成本工作量预测&#x…

vscode移动侧边栏到右边

vscode移动侧边栏到右边&#xff0c;的简单办法 直接在侧栏上单击右键&#xff0c;选择向右移动主侧栏

有哪些好的 Stable Diffusion 提示词(Prompt)可以参考?

Docker 作图咒语生成器 docker-prompt-generator 是一个开源项目&#xff0c;可以利用模型反推出提示词&#xff0c;让你偷偷懒&#xff0c;无需琢磨怎么写prompt&#xff0c;只需要找一个差不多的模型反推一下&#xff0c;直接用就好了&#xff0c;支持支持 MidJourney、Stab…