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

news/2024/7/18 21:47:06 标签: mvc, java
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

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

java">    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);
                }
            }
        }

定义处理结果

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

初始化

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

java">    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.niftyadmin.cn/n/5544484.html

相关文章

FFmpeg 实现从摄像头获取流并通过RTMP推流

使用FFmpeg库实现从USB摄像头获取流并通过RTMP推流&#xff0c;FFmpeg版本为4.4.2-0。RTMP服务器使用的是SRS&#xff0c;拉流端使用VLC。如果想降低延时&#xff0c;首先需要配置SRS为Realtime模式&#xff0c;拉流的话就不要用VLC了&#xff0c;使用 ffplay 来拉流播放&#…

go-redis 封装事件-client封装模型、批量数据处理的导出器设计

一、redis-go的封装实践-client模型 // Copyright 2020 Lingfei Kong <colin404foxmail.com>. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file.package storageimport ("context&q…

Python 爬虫 tiktok API接口获取tiktok用户关注列表

此接口可获取tiktok用户关注列表。若有需要&#xff0c;请点击文末链接联系我们。 详细采集页面如下https://www.tiktok.com/quanap_official 请求API http://api.xxxx.com/tt/user/following?user_id7252644648840381445&count10&offset0&tokentest 请求参数 返…

Django 靓号管理系统:表结构设计与初始化

在本文中,我们将介绍如何为一个靓号管理系统设计和初始化数据库表结构。这个系统包括部门、管理员和靓号三个主要实体。我们将使用 Django 的模型系统来定义这些表结构。 1. 项目初始化 首先,让我们创建一个新的 Django 项目和应用: django-admin startproject number cd…

可灵AI快速迭代:揭示中国AI技术的全球领先地位

最近&#xff0c;中国企业在AI技术上的快速迭代引起了广泛关注。以可灵AI为例&#xff0c;一个月内连续三次升级&#xff0c;推出了高清版和首尾帧控制等功能。这种高频率的技术更新&#xff0c;不仅显示了中国企业的拼劲&#xff0c;也对全球AI竞赛产生了深远影响。 中国企业的…

十一、作业

1.从大到小输出 写代码将三个整数数按从大到小输出。 void Swap(int* px, int* py) {int tmp *px;*px *py;*py tmp;} int main() {int a 0;int b 0;int c 0;scanf("%d %d %d", &a, &b, &c);int n 0;if (a<b){Swap(&a, &b);}if (a &l…

判断对象能否回收的两种方法,以及JVM引用

判断对象能否回收的两种方法&#xff1a;引用计数算法&#xff0c;可达性分析算法 引用计数算法&#xff1a;给对象添加一个引用计数器&#xff0c;当该对象被其它对象引用时计数加一&#xff0c;引用失效时计数减一&#xff0c;计数为0时&#xff0c;可以回收。 特点&#xf…

[QT入门]树形视图控件

一、概述 Qt中的QTreeView控件是一个基于项模型的树形视图&#xff0c;它提供了一种展示分层数据结构的方式。与QListView和QTableView相比&#xff0c;QTreeView更适合展示具有层级关系的数据&#xff0c;如文件系统、组织结构等。 二、基本使用 1.创建QTreeView实例&#xf…