Spring学习(三):MVC

news2024/11/25 15:48:54

一、什么是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 {

    @Override
    protected 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 {
    ...
    @Override
    protected 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;

    @Override
    public void init() throws ServletException {
        this.getMappings = scanGetInControllers();
        this.postMappings = scanPostInControllers();
        this.viewEngine = new ViewEngine(getServletContext());
    }
    ...
}

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

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

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

相关文章

WPS 换行后缩进、加粗等自定义样式的清除

在使用WPS的Word编辑文字时&#xff0c;经常会出现换行后&#xff0c;自动缩进这种格式&#xff0c;需要清除这种样式&#xff0c;可以按照下图来操作&#xff1a;

无锡矽杰微电子----XC8P9520MCU之项目开发

上篇文章说了开发环境和烧录环境的配置以及安装&#xff0c;那么接下来就需要开发项目了。 Demo文件资料-无锡矽杰微电子有限公司 (xjmcu.com) 这里贴上矽杰微官网的资料下载专区&#xff0c;包括不同芯片的DEMO和上文所说的工具。 既然是项目开发&#xff0c;那么芯片的数据手…

SaaS架构C/S检验科LIS系统源码: 检验申请、标本编号、联机采集

适用于医院检验科实际需要的LIS管理系统, 实现检验业务全流程的计算机管理。从检验申请、标本编号、联机采集、中文报告单的生成与打印、质控图的绘制和数据的检索与备份。通过将所有仪器自身提供的端口与科室LIS系统中的工作站点连接,实现与医院HIS系统的对接。 通过门诊医生和…

无涯教程-JavaScript - MDETERM函数

描述 MDETERM函数返回数组的矩阵行列式。 语法 MDETERM (array)争论 Argument描述Required/OptionalArrayA numeric array with an equal number of rows and columns.Required Notes 数组可以作为单元格范围给出,如A1:C3;作为数组常量,如{1,2,3; 4,5,6; 7,8,9}&#xff1…

工业物联网大数据解决方案:排水设备远程监控和大数据统计系统

一、项目背景 给排水系统&#xff0c;作为城市的基础设施建设&#xff0c;是居民生产生活的必要保障。由于给排水系统通常站点零散分布&#xff0c;运维管理涉及的区域广泛&#xff0c;水位、流量、机泵运行等运行参数的测报&#xff0c;目前采取人工测量的&#xff0c;上令下…

如何评价低代码平台在企业复杂应用场景中的适用性?

随着编程语言的不断迭代、抽象、简化和整合&#xff0c;低代码技术正不断精进&#xff0c;形成更为简单清晰的图形化界面与高级语言结合的开发模式。在数字化转型方案的实施过程中&#xff0c;低代码开发广泛适用于各种应用场景&#xff0c;能够减少繁琐的重复性代码编写工作&a…

Docker安装与卸载

Docker安装与卸载 安装 yum install -y yum-utils \device-mapper-persistent-data \lvm2 --skip-broken更新本地镜像源 打开终端或 SSH 连接到 Rocky Linux 的服务器。 进入 /etc/yum.repos.d/ 目录&#xff0c;该目录包含 Rocky Linux 的 yum 配置文件。 cd /etc/yum.repo…

二维码智慧门牌管理系统:提升城市管理效率与便捷性

文章目录 前言一、二维码智慧门牌管理系统的基本原理二、二维码智慧门牌管理系统的开发解决方案三、二维码智慧门牌管理系统的优势 前言 随着科技的不断发展&#xff0c;城市管理模式也在不断升级与优化。其中&#xff0c;二维码智慧门牌管理系统的出现&#xff0c;为城市管理…

电脑提示找不到msvcr120.dll无法继续执行代码,哪个修复方法更简单

电脑报错“找不到msvcr120.dll无法继续执行代码”。我相信&#xff0c;对于许多使用电脑的朋友来说&#xff0c;这个问题并不陌生。我们在使用电脑的过程中&#xff0c;可能会遇到各种各样的问题&#xff0c;其中就包括了找不到msvcr120.dll这个文件的问题。那么&#xff0c;这…

SF506DS-ASEMI超快恢复二极管SF506DS

编辑&#xff1a;ll SF506DS-ASEMI超快恢复二极管SF506DS 型号&#xff1a;SF506DS 品牌&#xff1a;ASEMI 封装&#xff1a;TO-252 特性&#xff1a;贴片、快恢复二极管 正向电流&#xff1a;5A 反向耐压&#xff1a;600V 恢复时间&#xff1a;35ns 引脚数量&#xf…

ICS TRIPLEX T8403调节器模块产品特点

ICS TRIPLEX T8403 是一款调节器模块&#xff0c;通常用于工业自动化和控制系统中&#xff0c;主要用于监测和控制过程变量以确保系统的稳定性和性能。以下是该产品的一些主要特点&#xff1a; 高精度调节&#xff1a; T8403 调节器模块通常具有高精度的控制功能&#xff0c;可…

网络安全(黑客技术)学习笔记

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

CDH大数据平台集群部署

文章目录 1. 资源准备2. 部署 Mariadb 数据库3. 安装CM服务4. 安装数据节点5. 登录CM系统 1. 资源准备 准备好CDH安装包资源&#xff0c;官方网站下载需要账号&#xff0c;如果没有账号可以去网上到处搜搜。主要涉及到的资源有&#xff1a; cloudera-manager-servercloudera-m…

IEEE模板中没有.bib相关内容怎么添加?

为了加深个人对该问题的记忆&#xff0c;特在此进行记录。 下图是IEEE某期刊提供的期刊模板&#xff0c;该模板来自于IEEE-Template Selector 从图中并没有看到bib文件&#xff0c;而在main.tex中也并没有相关引导&#xff0c;只是提到&#xff1a; 那如何添加呢&#xff1f;…

深度学习——所需知识二

深度学习——所需知识二 文章目录 前言一、微积分1.1. 导数和微分1.2. 偏导数1.3. 梯度1.4. 链式法则 二、自动微分2.1. 简单例子2.2. 非标量变量的反向传播2.3. 分离计算2.4. python控制流的梯度计算 三、概率3.1. 基本概率论3.1.1. 概率论公理3.1.2. 随机变量 3.2. 处理多个随…

关于mysql已有主从环境得主从重配置

恢复主从环境 1&#xff1a;Situation&#xff08;背景&#xff09;2&#xff1a;Task&#xff08;任务&#xff09;3&#xff1a;Action&#xff08;行动&#xff09;主库&#xff08;备份&#xff09;1:锁表2&#xff1a;效验是否锁表3&#xff1a;查看主库状态并记录4&#…

工作相关----《配置bond》

进入到/etc/sysconfig/network-scripts&#xff0c;按照要求配置主备关系 vim ifcfg-bond0&#xff0c;编写主要内容如下&#xff1a; /*mode1 表示主备份策略&#xff0c;miimon100 系统每100毫秒监测一次链路连接状态&#xff0c; 如果有一条线路不通就转入另一条线路*/ BOND…

抖音seo矩阵系统源码开发技术

抖音seo矩阵系统源码开发技术要求十分严格。首先&#xff0c;需要熟练掌握Python、Java等编程语言&#xff0c;具有扎实的算法基础。在此基础上&#xff0c;还需要具备深度学习、神经网络等相关技能&#xff0c;能够实现精准推荐和内容分析等功能。 其次&#xff0c;抖音seo矩…

剑指offer(C++)-JZ20:表示数值的字符串(算法-模拟)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 请实现一个函数用来判断字符串str是否表示数值&#xff08;包括科学计数法的数字&#xff0c;小数和整数&…

如何成为一个牛逼的脚本小子日记之0x001-JAVA 代码审计 Top half (2023829-...

如何成为一个牛逼的脚本小子日记之 0x001-JAVA 代码审计 Top half (2023/8/29-2023/9/1) 此记录是在拥有一定的java基础下进行的,java基础类,反射,继承,filter,servlet,calssLoader,Dynamic agent等基础知识点将不再进行阐述,TopHalf主要集中描述除了java反序列化之外的基础漏…