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(Model-View-Controller)是一种软件设计模式,用于组织和管理应用程序的代码结构。它将应用程序分为三个主要部分,即模型(Model)、视图(View)和控制器(…...
排查disabled问题之谷歌新版本特性
问题复现 最近我突然接手一个后台的bug,这个后台很久没有迭代更新了,我也不熟悉业务,所以只能看一下源码,问题很快就复现,测试的修复操作也很正确,就是因为渲染的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)代码解读: 用于改变PyTorch张量(tensor)y的形状的。 y.size(0)返回y的第一维的大小。 -1表示让PyTorch自动计算该维度的大小,以确保新的张量与原始张量有相同的元素数量。 功能:将y的第一维保持…...
必示科技赋能广发证券运维数字化实践案例,入选信通院《中国AIOps现状调查报告(2023)》
近期,“必示科技赋能广发证券运维数字化实践,打造智能运维数据中台”合作案例被中国信息通信研究院作为优秀金融案例项目,收录在最新的《中国AIOps现状调查报告(2023)》(金融行业仅3家)。 以必…...
特斯拉Dojo超算:AI训练平台的自动驾驶与通用人工智能之关键
特斯拉公开Dojo超算架构细节,AI训练算力平台成为其自动驾驶与通用人工智能布局的关键一环 在近日举行的Hot Chips 34会议上,特斯拉披露了其自主研发的AI超算Dojo的详细信息。Dojo是一个可定制的超级计算机,从芯片到系统全部由特斯拉自主设计…...
Linux中的一些常用命令
1.查看Linux系统中自带的GLIBC版本 ldd --version2.Linux中删除文件的命令 在Linux中,删除文件的命令是 rm。 使用 rm 命令时,请小心使用,因为它将直接删除文件,而不会将其移动到回收站。 以下是 rm 命令的一些常用选项&#…...
VRTK4⭐二.VRTK4的项目基础配置
文章目录 🟥 硬件基本配置🟧 设置XR Plug-in Management🟨 添加项目Tilia🟩 配置项目Hierarchy 🟥 硬件基本配置 解决使用OpenXR,HTC头显正常追踪,但手柄无法使用的问题. 问题如下: 当我们按照官方的标准流程配置完Op…...
word-doc和docx区别
office从业者路过。 文件结构上doc文件数据是以二进制形式存放的。 docx是以xml文件形式存放的。 doc兼容较差,docx效果更好。...
深度学习-偏导数复习
文章目录 前言1.偏导数2.偏导数概念1.对x的偏导数2.对y的偏导数3.多元函数偏导数4.如何计算偏导数1.二元函数的偏导数2.复杂函数的偏导数3.分段函数1.分界点的偏导数 5.偏导数与连续之间的关系6.偏导数的几何意义7.高阶偏导数1.定义2.高阶偏导数例题(二阶偏导数&…...
linux之jq命令
jq命令用于linux命令行对json进行处理 参数 option -r:去掉字符串的引号"例子 tt.json文件如下: [{"metric": "httpcode","tags": {"cluster": "tt","domain": "www.baidu.…...
nginx知识点详解:反向代理+负载均衡+动静分离+高可用集群
一、nginx基本概念 1. nginx是什么,做什么事情? Nginx是一个高性能的HTTP和反向代理服务器,特点是占有内存少,并发能力强。Nginx转为性能优化而开发,能经受高负载考验。支持热部署,启动容易,运…...
powerDesigner 的基本使用
打开powerDesigner 新建 PDM(物理数据模型) 添加表字段 双击表,设置ID自增 选择导出数据库表SQL 导出成功 使用三方工具连接数据库,然后运行对应SQL文件即可 导入SQL文件数据到powerDesigner...
Java下打印一个等腰三角型
想达到这个结果,通常的做法是通过拼结两个三角型达到,但是实际上还有最右边的第三个三角型没有处理,这个拼结的方法总让人看起来有一点不完美的感觉,于是我自创了一个思路,一气合成,代码如下(本…...
HR的职业规划
CHRO可以说是HR职业发展的天花板了。CHRO对一个企业来说至关重要,是CEO的左膀右臂。那从CEO的角度来看CHRO,应该具备什么样的素质和能力,又能为公司带来什么样的价值呢? 在公司的不同发展阶段,HR部门有着不同的战略和…...
avi怎么转换成视频?
avi怎么转换成视频?在我们日常使用的视频格式中,AVI是一种常见且经常被使用的音频视频交叉格式之一。它的优点之一是占用的存储空间相对较小,但也明显存在着画质损失的缺点。虽然AVI格式的视频在某种程度上也很常见,但与最常见的M…...
爬虫数据存储:技术、策略与实践(一)
文章目录 🍋引言🍋xlrd库和xlwt库🍋创建Excel文件🍋通过Python代码向Excel写入数据🍋案例实战 🍋引言 本节主要介绍一下在使用网络爬虫技术的时候,如何将数据存储到Excel中去 🍋xl…...
【音视频】ffplay解析-音视频同步
音视频同步 主要解析:以音频为基准,让视频合成音频 思路 视频慢了则丢掉部分视频帧(视觉->画⾯跳帧) 视频快了则继续渲染上⼀帧 具体实现 一个国际标准:音频帧-视频帧时间戳的差值在-100ms~25ms内流畅 1.差值音频…...
虚拟列表 - Vue3实现一个可动态改变高度的虚拟滚动列表
虚拟列表 - Vue3实现一个可动态改变高度的虚拟滚动列表 前言 在开发中经常遇到大量的渲染列表数据问题,往往我们就只是简单地遍历渲染,没有过多地去关注是否会存在性能问题,这导致如果数据量较大的时候,比如上万条数据ÿ…...
PyTorch实战:实现Cifar10彩色图片分类
目录 前言 一、Cifar10数据集 class torch.utils.data.Dataset torch.utils.data.DataLoader 二、定义神经网络 普通神经网络: 定义损失函数和优化器 训练网络-Net CPU训练 模型准确率 编辑 GPU训练 训练网络-LeNet 模型准确率 点关注,防走丢&#x…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...
