对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) {}

初始化

   @Overridepublic 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.mzph.cn/web/38623.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

强化学习-5 策略梯度、Actor-Critic 算法

文章目录 1 基于价值&#xff08; value-based \text{value-based} value-based &#xff09;算法的缺点2 策略梯度算法2.1 解释2.1.1 分母和分子相消2.1.2 对数函数的导数2.1.3 组合公式2.1.4 总结 3 REINFORCE算法4 策略梯度推导进阶4.1 平稳分布4.2 基于平稳分布的策略梯度…

Python游戏脚本开发之大漠插件

自动化 文章目录 自动化前言一、开发环境二、免注册调用三、创建大漠对象四、注册五、大漠功能调用六、整体代码 前言 大漠插件是集前后台&#xff0c;文字识别&#xff0c;图色&#xff0c;键鼠&#xff0c;窗口&#xff0c;内存&#xff0c;DX&#xff0c;Call等功能于一身的…

HSP_13章 Python_魔术方法

文章目录 P132 魔术方法1. 魔术方法的基本介绍2. 常见的魔术方法2.1 __str__2.2 __eq__2.3 其它的几个魔术方法 P132 魔术方法 参考文档 1. 魔术方法的基本介绍 2. 常见的魔术方法 2.1 str # 请输出Monster[name&#xff0c;job&#xff0c;sal]对象的属性信息 #可以根据需要…

(1, ‘[SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1124)

WARNING: Retrying (Retry(total4, connectNone, readNone, redirectNone, statusNone)) after connection broken by SSLError(SSLError(1, [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1124))): /pypi/simple/urllib3/ pip install的时候遇到这个错误 第一步…

竞赛选题 交通目标检测-行人车辆检测流量计数 - 竞赛选题

文章目录 0 前言1\. 目标检测概况1.1 什么是目标检测&#xff1f;1.2 发展阶段 2\. 行人检测2.1 行人检测简介2.2 行人检测技术难点2.3 行人检测实现效果2.4 关键代码-训练过程 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 毕业设计…

OtterTune dead,参数调优还有DBdoctor

前言 数据库性能调优这块必不可少的环节是进行参数调优&#xff0c;数据库的参数很多&#xff0c;比如Oracle甚至有上千个参数&#xff0c;如何进行参数设置非常关键。参数调优需要基于业务的负载模型、硬件配置等多种因素&#xff0c;好的参数调优可以让数据库性能整体吞吐提…

Symfony配置管理深度解析:构建可维护项目的秘诀

Symfony是一个高度灵活且功能丰富的PHP框架&#xff0c;它提供了一套强大的配置管理系统&#xff0c;使得开发者能够轻松定制和优化应用程序的行为。本文将深入探讨Symfony中的配置管理机制&#xff0c;包括配置的结构、来源、加载过程以及最佳实践。 一、配置管理的重要性 在…

Scala类型类(Type Classes):灵活而强大的类型增强术

&#x1f31f; Scala类型类(Type Classes)&#xff1a;灵活而强大的类型增强术 在Scala编程语言中&#xff0c;类型类是一种强大的特性&#xff0c;它允许开发者以类型安全的方式扩展语言。类型类提供了一种机制&#xff0c;可以将行为与类型关联起来&#xff0c;类似于Java中…

【笔记】Android Settings 应用设置菜单的界面代码介绍

简介 Settings应用中&#xff0c;提供多类设置菜单入口&#xff0c;每个菜单内又有各模块功能的实现。 那么各个模块基于Settings 基础的界面Fragment去实现UI&#xff0c;层层按不同业务进行封装继承实现子类&#xff1a; DashboardFragmentSettingsPreferenceFragment 功…

植物大战僵尸杂交版,最新安装包(PC+手机+苹果)+ 修改器+高清工具

植物大战僵尸杂交版&#xff1a;全新游戏体验与创意碰撞 游戏简介 《植物大战僵尸杂交版》是由B站知名UP主潜艇伟伟迷基于经典游戏《植物大战僵尸》进行的一次大胆且富有创意的二次创作。这款游戏不仅保留了原版游戏的经典玩法&#xff0c;还融入了植物杂交的全新概念&#x…

Qt扫盲-QRect矩形描述类

QRect矩形描述总结 一、概述二、常用函数1. 移动类2. 属性函数3. 判断4. 比较计算 三、渲染三、坐标 一、概述 QRect类使用整数精度在平面中定义一个矩形。在绘图的时候经常使用&#xff0c;作为一个二维的参数描述类。 一个矩形主要有两个重要属性&#xff0c;一个是坐标&am…

同步互斥与通信

目录 一、同步与互斥的概念 二、同步与互斥并不简单 三、各类方法的对比 一、同步与互斥的概念 一句话理解同步与互斥&#xff1a;我等你用完厕所&#xff0c;我再用厕所。 什么叫同步&#xff1f;就是&#xff1a;哎哎哎&#xff0c;我正在用厕所&#xff0c;你等会。 什…

【实战场景】记一次UAT jvm故障排查经历

【实战场景】记一次UAT jvm故障排查经历 开篇词&#xff1a;干货篇&#xff1a;1.查看系统资源使用情况2.将十进制进程号转成十六进制3.使用jstack工具监视进程的垃圾回收情况4.输出指定线程的堆内存信息5.观察日志6.本地环境复现 总结篇&#xff1a;我是杰叔叔&#xff0c;一名…

线下促销折扣视频介绍

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货、宠物等连锁店使用。 详细介绍请…

Linux上systemctl 和 service 两个命令的区别和联系

systemctl 和 service 两个命令都是 Linux 系统中用于管理服务的工具&#xff0c;但它们分别关联着不同的初始化系统&#xff08;init system&#xff09;&#xff0c;并且在功能和使用场景上有所差异。 service 命令 关联的初始化系统&#xff1a;service 命令通常与 SysV i…

Python从零学习笔记(1)

1pip无法调用 刚入python&#xff0c;需要用到第三方模块&#xff0c;但是按照教程使用>>>pip install 总是出现错误提示 网上查询许久&#xff1a;语句没错&#xff1b;安装没错&#xff1b;环境配置也正常 最后才知道是不能先进入python模式&#xff0c;而是使用p…

2024年道路运输安全员考试题库及答案

一、多选题 11.《放射性物品安全管理条例》规定&#xff0c;运输放射性物品时&#xff0c;应当使用专用的放射性物品运输包装容器。在运输过程中正确的做法有&#xff08; &#xff09;。 A.托运人和承运人应当按照国家放射性物品运输安全标准和国家有关规定&#xff0c;在…

什么是定时器?

前言&#x1f440;~ 上一章我们介绍了阻塞队列以及生产者消息模式&#xff0c;今天我们来讲讲定时器 定时器 标准库中的定时器 schedule()方法 扫描线程 手动实现定时器 任务类 存储任务的数据结构 定时器类 如果各位对文章的内容感兴趣的话&#xff0c;请点点小赞&am…

【Python】列表

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

浅谈什么是计算机科学与技术(Computer Science,CS)

计算机科学的核心内容 计算机科学&#xff08;Computer Science, CS&#xff09;涵盖了以下主要领域&#xff1a; 硬件&#xff1a;涉及数字电路、集成电路、存储器和硬件设计与验证方法等。 例子&#xff1a;学习如何设计和实现一个简单的CPU&#xff0c;包括理解指令集、时钟…