当前位置: 首页 > news >正文

Spring学习(三):MVC

一、什么是MVC

MVC(Model-View-Controller)是一种软件设计模式,用于组织和管理应用程序的代码结构。它将应用程序分为三个主要部分,即模型(Model)、视图(View)和控制器(Controller),每个部分都有特定的职责和功能。

以下是 MVC 模式中各个组成部分的概述:

  • 模型(Model):模型代表应用程序的数据和业务逻辑。它负责处理数据的读取、存储、验证和处理,以及执行应用程序的核心业务逻辑。模型通常是独立于用户界面的,可以被多个视图和控制器共享。
  • 视图(View):视图是用户界面的表示,负责展示数据给用户,并接收用户的输入。它通常是模型的可视化表现形式,负责将模型的数据呈现给用户,并根据用户的操作更新界面。视图不处理业务逻辑,只负责显示和接收用户的操作。
  • 控制器(Controller):控制器是模型和视图之间的协调者,负责处理用户的输入、更新模型的数据以及更新视图的显示。它接收用户的操作请求,调用相应的模型方法进行数据处理和更新,并在必要时更新视图以反映最新的数据。

MVC 的核心思想是将应用程序的逻辑和数据分离,使其更易于理解、扩展和维护。通过将应用程序的不同部分分离,MVC 模式提供了更好的代码组织和可重用性。在 MVC 中,用户与视图进行交互,视图通过控制器将用户的操作转发给模型进行处理,模型根据业务逻辑进行数据处理,然后通知视图进行更新。这种分离和协作的方式使得应用程序的不同部分能够独立地开发和测试,同时也提高了代码的可维护性和重用性。

在这里插入图片描述
举例来说,当浏览器发送一个查询请求,要求查询用户信息时,Controller通过jdbc调用数据库方法获得对应的User对象,然后将user对象传递给user.jsp渲染,并发送回浏览器。


二、Servlet

Servlet 是 Java 编程语言中的一种特殊类,用于处理 Web 应用程序中的动态内容和 HTTP 请求。Servlet 提供了一种基于服务器的编程模型,允许开发者通过编写 Java 代码来处理客户端的请求并生成相应的响应。

实际上Servlet就是一个API接口,它需要底层的Web服务器实现HTTP协议的解析处理,但也以此将底层解析代码对开发者屏蔽。使用者只需要关注上层的api接口的调用即可。我们使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能。

用法关键在于继承HttpServlet,覆写doPost, doGet等方法,并调用业务方法,返回HttpResponse。

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;// WebServlet注解表示这是一个Servlet,并映射到地址/:
@WebServlet(urlPatterns = "/")
public class UserServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {String action = request.getParameter("action");if (action != null) {switch (action) {case "register":handleRegistration(request, response);break;case "login":handleLogin(request, response);break;default:response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid action");}} else {response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Action parameter is missing");}}private void handleRegistration(HttpServletRequest request, HttpServletResponse response) throws IOException {// 处理用户注册逻辑// 从 request 中获取用户提交的注册信息String username = request.getParameter("username");String password = request.getParameter("password");// 执行用户注册操作,例如将用户信息存储到数据库中// 返回注册成功的响应response.setContentType("text/html");PrintWriter out = response.getWriter();out.println("<html><body>");out.println("<h2>Registration successful</h2>");out.println("<p>Welcome, " + username + "!</p>");out.println("</body></html>");}private void handleLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {// 处理用户登录逻辑// 从 request 中获取用户提交的登录信息String username = request.getParameter("username");String password = request.getParameter("password");// 执行用户登录验证操作,例如从数据库中检查用户名和密码是否匹配// 返回登录成功或失败的响应response.setContentType("text/html");PrintWriter out = response.getWriter();out.println("<html><body>");if (username.equals("admin") && password.equals("password")) {out.println("<h2>Login successful</h2>");out.println("<p>Welcome back, " + username + "!</p>");} else {out.println("<h2>Login failed</h2>");out.println("<p>Invalid username or password.</p>");}out.println("</body></html>");}
}

UserServlet 类继承了 HttpServlet 并重写了 doPost 方法来处理客户端的 POST 请求。根据请求中的 action 参数的不同值,分别调用 handleRegistration 和 handleLogin 方法来处理用户注册和登录逻辑。

要将Servlet部署到支持Servlet api的web服务器(Servlet容器如 Apache Tomcat),你需要将编译后的类文件(例如 UserServlet.class)放置在正确的目录结构中,并在 web.xml 文件(位于 WEB-INF 目录下)中进行配置。

具体配置参考 lxf Servlet教程


三、MVC框架原理和实现

参考lxf mvc高级开发
在使用Servlet的案例中,我们可以注意到,一个Servlet只能处理一个url下的Get Post请求,例如如果一个 Servlet 映射到路径 /users,它将处理所有以 /users 开头的请求路径。

如果有一个MVC框架,能够通过一个底层的DispatchServlet,存储所有的url到方法的映射,那就不需要重复继承和编写Servlet相关代码,上层被映射的方法可以组织成一个纯粹的Java类,只需要关注control部分的业务逻辑即可

public class UserController {@GetMapping("/signin")public ModelAndView signin() {...}@PostMapping("/signin")public ModelAndView doSignin(SignInBean bean) {...}@GetMapping("/signout")public ModelAndView signout(HttpSession session) {...}
}

以以上代码为例,如果Servlet可以直接将doGet中与业务逻辑无关的内容实现,把Controller业务逻辑需要的功能抽象为新的类,返回值再通过ModelAndView传送给Servlet,由Servlet交给底层渲染引擎得到View,就可以使代码更加简洁,扩展性更强。

MVC框架原理:

我们需要在MVC框架中创建一个接收所有请求的Servlet,通常我们把它命名为DispatcherServlet,它总是映射到/,然后,根据不同的Controller的方法定义的@Get或@Post的Path决定调用哪个方法,最后,获得方法返回的ModelAndView后,渲染模板,写入HttpServletResponse,即完成了整个MVC的处理。

结构如下
在这里插入图片描述
DispatchServlet编写

@WebServlet(urlPatterns = "/")
public class DispatcherServlet extends HttpServlet {private Map<String, GetDispatcher> getMappings = new HashMap<>(); //需要存储请求路径到某个具体方法的映射private Map<String, PostDispatcher> postMappings = new HashMap<>();
}//处理一个GET请求是通过GetDispatcher对象完成的,它需要如下信息
class GetDispatcher {Object instance; // Controller实例Method method; // Controller方法String[] parameterNames; // 方法参数名称Class<?>[] parameterClasses; // 方法参数类型
}

使用invoke处理真正的请求

class GetDispatcher {...public ModelAndView invoke(HttpServletRequest request, HttpServletResponse response) {Object[] arguments = new Object[parameterClasses.length];for (int i = 0; i < parameterClasses.length; i++) {String parameterName = parameterNames[i];Class<?> parameterClass = parameterClasses[i];if (parameterClass == HttpServletRequest.class) {arguments[i] = request;} else if (parameterClass == HttpServletResponse.class) {arguments[i] = response;} else if (parameterClass == HttpSession.class) {arguments[i] = request.getSession();} else if (parameterClass == int.class) {arguments[i] = Integer.valueOf(getOrDefault(request, parameterName, "0"));} else if (parameterClass == long.class) {arguments[i] = Long.valueOf(getOrDefault(request, parameterName, "0"));} else if (parameterClass == boolean.class) {arguments[i] = Boolean.valueOf(getOrDefault(request, parameterName, "false"));} else if (parameterClass == String.class) {arguments[i] = getOrDefault(request, parameterName, "");} else {throw new RuntimeException("Missing handler for type: " + parameterClass);}}return (ModelAndView) this.method.invoke(this.instance, arguments);}private String getOrDefault(HttpServletRequest request, String name, String defaultValue) {String s = request.getParameter(name);return s == null ? defaultValue : s;}
}

Dispatch核心流程

public class DispatcherServlet extends HttpServlet {...@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/html");resp.setCharacterEncoding("UTF-8");String path = req.getRequestURI().substring(req.getContextPath().length());// 根据路径查找GetDispatcher:GetDispatcher dispatcher = this.getMappings.get(path);if (dispatcher == null) {// 未找到返回404:resp.sendError(404);return;}// 调用Controller方法获得返回值:ModelAndView mv = dispatcher.invoke(req, resp);// 允许返回null:if (mv == null) {return;}// 允许返回`redirect:`开头的view表示重定向:if (mv.view.startsWith("redirect:")) {resp.sendRedirect(mv.view.substring(9));return;}// 将模板引擎渲染的内容写入响应:PrintWriter pw = resp.getWriter();this.viewEngine.render(mv, pw);pw.flush();}
}

这样使得上层代码编写更灵活。例如,一个显示用户资料的请求可以这样写

@GetMapping("/user/profile")
public ModelAndView profile(HttpServletResponse response, HttpSession session) {User user = (User) session.getAttribute("user");if (user == null) {// 未登录,跳转到登录页:return new ModelAndView("redirect:/signin");}if (!user.isManager()) {// 权限不够,返回403:response.sendError(403);return null;}return new ModelAndView("/profile.html", Map.of("user", user));
}

最后一步是在DispatcherServlet的init()方法中初始化所有Get和Post的映射,以及用于渲染的模板引擎:

public class DispatcherServlet extends HttpServlet {private Map<String, GetDispatcher> getMappings = new HashMap<>();private Map<String, PostDispatcher> postMappings = new HashMap<>();private ViewEngine viewEngine;@Overridepublic void init() throws ServletException {this.getMappings = scanGetInControllers();this.postMappings = scanPostInControllers();this.viewEngine = new ViewEngine(getServletContext());}...
}

如何扫描所有Controller以获取所有标记有@GetMapping和@PostMapping的方法,使用反射.

相关文章:

Spring学习(三):MVC

一、什么是MVC MVC&#xff08;Model-View-Controller&#xff09;是一种软件设计模式&#xff0c;用于组织和管理应用程序的代码结构。它将应用程序分为三个主要部分&#xff0c;即模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;…...

排查disabled问题之谷歌新版本特性

问题复现 最近我突然接手一个后台的bug&#xff0c;这个后台很久没有迭代更新了&#xff0c;我也不熟悉业务&#xff0c;所以只能看一下源码&#xff0c;问题很快就复现&#xff0c;测试的修复操作也很正确&#xff0c;就是因为渲染的input标签中存在disableddisabled’属性导…...

三、开发工具

开发工具 开发工具1.1.熟悉IDEA1.2.下载IDEA1.3.IDEA中文插件1.4.IDEA输出中文乱码1.5.使用IDEA —————————————————————————————————————————————————— —————————————————————————————————…...

代码解读:y.view(y.size(0), -1)---tensor张量第一维保持不变,其余维度展平

y.view(y.size(0), -1)代码解读&#xff1a; 用于改变PyTorch张量&#xff08;tensor&#xff09;y的形状的。 y.size(0)返回y的第一维的大小。 -1表示让PyTorch自动计算该维度的大小&#xff0c;以确保新的张量与原始张量有相同的元素数量。 功能&#xff1a;将y的第一维保持…...

必示科技赋能广发证券运维数字化实践案例,入选信通院《中国AIOps现状调查报告(2023)》

近期&#xff0c;“必示科技赋能广发证券运维数字化实践&#xff0c;打造智能运维数据中台”合作案例被中国信息通信研究院作为优秀金融案例项目&#xff0c;收录在最新的《中国AIOps现状调查报告&#xff08;2023&#xff09;》&#xff08;金融行业仅3家&#xff09;。 以必…...

特斯拉Dojo超算:AI训练平台的自动驾驶与通用人工智能之关键

特斯拉公开Dojo超算架构细节&#xff0c;AI训练算力平台成为其自动驾驶与通用人工智能布局的关键一环 在近日举行的Hot Chips 34会议上&#xff0c;特斯拉披露了其自主研发的AI超算Dojo的详细信息。Dojo是一个可定制的超级计算机&#xff0c;从芯片到系统全部由特斯拉自主设计…...

Linux中的一些常用命令

1.查看Linux系统中自带的GLIBC版本 ldd --version2.Linux中删除文件的命令 在Linux中&#xff0c;删除文件的命令是 rm。 使用 rm 命令时&#xff0c;请小心使用&#xff0c;因为它将直接删除文件&#xff0c;而不会将其移动到回收站。 以下是 rm 命令的一些常用选项&#…...

VRTK4⭐二.VRTK4的项目基础配置

文章目录 &#x1f7e5; 硬件基本配置&#x1f7e7; 设置XR Plug-in Management&#x1f7e8; 添加项目Tilia&#x1f7e9; 配置项目Hierarchy &#x1f7e5; 硬件基本配置 解决使用OpenXR,HTC头显正常追踪,但手柄无法使用的问题. 问题如下: 当我们按照官方的标准流程配置完Op…...

word-doc和docx区别

office从业者路过。 文件结构上doc文件数据是以二进制形式存放的。 docx是以xml文件形式存放的。 doc兼容较差&#xff0c;docx效果更好。...

深度学习-偏导数复习

文章目录 前言1.偏导数2.偏导数概念1.对x的偏导数2.对y的偏导数3.多元函数偏导数4.如何计算偏导数1.二元函数的偏导数2.复杂函数的偏导数3.分段函数1.分界点的偏导数 5.偏导数与连续之间的关系6.偏导数的几何意义7.高阶偏导数1.定义2.高阶偏导数例题&#xff08;二阶偏导数&…...

linux之jq命令

jq命令用于linux命令行对json进行处理 参数 option -r&#xff1a;去掉字符串的引号"例子 tt.json文件如下&#xff1a; [{"metric": "httpcode","tags": {"cluster": "tt","domain": "www.baidu.…...

nginx知识点详解:反向代理+负载均衡+动静分离+高可用集群

一、nginx基本概念 1. nginx是什么&#xff0c;做什么事情&#xff1f; Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;特点是占有内存少&#xff0c;并发能力强。Nginx转为性能优化而开发&#xff0c;能经受高负载考验。支持热部署&#xff0c;启动容易&#xff0c;运…...

powerDesigner 的基本使用

打开powerDesigner 新建 PDM(物理数据模型) 添加表字段 双击表&#xff0c;设置ID自增 选择导出数据库表SQL 导出成功 使用三方工具连接数据库&#xff0c;然后运行对应SQL文件即可 导入SQL文件数据到powerDesigner...

Java下打印一个等腰三角型

想达到这个结果&#xff0c;通常的做法是通过拼结两个三角型达到&#xff0c;但是实际上还有最右边的第三个三角型没有处理&#xff0c;这个拼结的方法总让人看起来有一点不完美的感觉&#xff0c;于是我自创了一个思路&#xff0c;一气合成&#xff0c;代码如下&#xff08;本…...

HR的职业规划

CHRO可以说是HR职业发展的天花板了。CHRO对一个企业来说至关重要&#xff0c;是CEO的左膀右臂。那从CEO的角度来看CHRO&#xff0c;应该具备什么样的素质和能力&#xff0c;又能为公司带来什么样的价值呢&#xff1f; 在公司的不同发展阶段&#xff0c;HR部门有着不同的战略和…...

avi怎么转换成视频?

avi怎么转换成视频&#xff1f;在我们日常使用的视频格式中&#xff0c;AVI是一种常见且经常被使用的音频视频交叉格式之一。它的优点之一是占用的存储空间相对较小&#xff0c;但也明显存在着画质损失的缺点。虽然AVI格式的视频在某种程度上也很常见&#xff0c;但与最常见的M…...

爬虫数据存储:技术、策略与实践(一)

文章目录 &#x1f34b;引言&#x1f34b;xlrd库和xlwt库&#x1f34b;创建Excel文件&#x1f34b;通过Python代码向Excel写入数据&#x1f34b;案例实战 &#x1f34b;引言 本节主要介绍一下在使用网络爬虫技术的时候&#xff0c;如何将数据存储到Excel中去 &#x1f34b;xl…...

【音视频】ffplay解析-音视频同步

音视频同步 主要解析&#xff1a;以音频为基准&#xff0c;让视频合成音频 思路 视频慢了则丢掉部分视频帧&#xff08;视觉->画⾯跳帧&#xff09; 视频快了则继续渲染上⼀帧 具体实现 一个国际标准&#xff1a;音频帧-视频帧时间戳的差值在-100ms~25ms内流畅 1.差值音频…...

虚拟列表 - Vue3实现一个可动态改变高度的虚拟滚动列表

虚拟列表 - Vue3实现一个可动态改变高度的虚拟滚动列表 前言 在开发中经常遇到大量的渲染列表数据问题&#xff0c;往往我们就只是简单地遍历渲染&#xff0c;没有过多地去关注是否会存在性能问题&#xff0c;这导致如果数据量较大的时候&#xff0c;比如上万条数据&#xff…...

PyTorch实战:实现Cifar10彩色图片分类

目录 前言 一、Cifar10数据集 class torch.utils.data.Dataset torch.utils.data.DataLoader 二、定义神经网络 普通神经网络: 定义损失函数和优化器 训练网络-Net CPU训练 模型准确率 ​编辑 GPU训练 训练网络-LeNet 模型准确率 点关注&#xff0c;防走丢&#x…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Python爬虫实战:研究Restkit库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...