【Java Web编程 十四】深入理解MVC架构模式(一)

简介: 【Java Web编程 十四】深入理解MVC架构模式(一)

前面的十一篇博客把JSP和Servlet基本都详细的介绍了一遍,终于,我们的MVC架构模式横空出世,这也是框架诞生前的古早味Web程序的基本设计方式,事实上,即使框架诞生后,也不过是基于MVC模式的扩充和强化,所以理解MVC思想以及MVC早期的实现方式对于后续的框架学习以及理解Web开发的历史有很大的帮助。这篇Blog我将以一个简单的【用户注册登录】实践为主来串联之前十一篇Blog里提到的全部知识,在使用层面为Java Web编程系列划上一个句号。

MVC思想

MVC 全名是 Model View Controller,一种软件设计典范,用一种业务数据、逻辑、界面显示分离的方法组织代码,各部分职责如下:

  • 视图
    视图是用户看到并与之交互的界面。对老式的Web应用程序来说,视图就是由HTML元素组成的界面,在新式的Web应用程序中,HTML依旧在视图中扮演着重要的角色,但是已经被一些能显示动态数据的JSP逐步取代了,MVC好处是它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,不管这些数据来源是什么,作为视图来讲,它只是作为一种输出数据并允许用户操纵的方式
  • 模型
    模型表示企业数据和业务规则。在MVC的三个部件中,模型拥有最多的处理任务。被模型返回的数据是中立的,就是说模型与数据格式无关,这样一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。不能将模型看出一个只有数据的类,其实模型也包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),现在一般会更加细分:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据业务两部分
  • 控制器
    控制器接受用户的输入并调用模型和视图去完成用户的需求,所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数据。

在前后端不分离的站点中,JSP充当了视图,Servlet充当了控制器,Model充当了模型。抽象意义上的职责如下图所示:

现在的MVC模型已经演化为如下操作过程,下面的整个项目就是依据这样的分层构建的

用户请求会先到达页面,填写信息后进入控制层流转,逻辑处理在Model层做,注意Model层也分两类:

  • Service:处理业务逻辑,这里的Model会返回给前端
  • Dao:处理持久化数据,这里的Model类似POJO,和数据库内的字段一一对应

当然对于我相对简单的一个系统,用一个贯穿的Model就可以了,但最后做解耦,因为落库的字段不一定和请求过来的字段完全一致。

项目构建

其实这个项目是5年前导师交给的一个任务,只完成了前端,当时用的是bootstrap实现的,后端完全没有,这次刚好学习完Java Web就把这部分补充上,项目名称为【黄金找矿网站】。

项目要求

在正式做之前先确定一下项目的要求,要求完成注册登录任务,任务需要满足如下几个要点:

  1. 未登录之前,除了注册登录页面,站点的其它页面均不能访问,请求时都自动跳转到登录页面
  2. 注册用户信息时,需要有前后端验证,对用户提交的表单进行表单验证
  3. 表单验证失败后收集错误信息跳转到错误页面,告知失败原因
  4. 注册成功后跳转到注册成功页面,告知用户的注册信息
  5. 登录时判断库里是否有该用户,只有存在且密码正确才可以登录成功
  6. 点击登出后当前用户恢复未登录状态
  7. 站点开启和关闭时需要加监控,监控服务的运行状态

其实这样一个注册登录项目就是我这两天设计出来用来将历史十一篇blog学习过的技术进行整合的,每个要求都涉及相关知识点。

技术要点

对照上边的实现要求,其实可以能抽象出所有的技术要点:

  1. 未登录之前,除了注册登录页面,站点的其它页面均不能访问,请求时都自动跳转到登录页面【Filter过滤器&重定向&Session作用域
  2. 注册用户信息时,需要有前后端验证,对用户提交的表单进行表单验证【表单提交&表单验证&请求转发&Servlet常用对象
  3. 表单验证失败后收集错误信息跳转到错误页面,告知失败原因【JSTL标签
  4. 注册成功后跳转到注册成功页面,告知用户的注册信息【JSP语法&EL表达式&JavaBean
  5. 登录时判断库里是否有该用户,只有存在且密码正确才可以登录成功【JDBC
  6. 点击登出后当前用户恢复未登录状态【Session销毁
  7. 站点开启和关闭时需要加监控,监控服务的运行状态【Listener监听器

好的,也就是说这个项目可以帮助我和大家复习整体的古早Java Web实现方式。

整体布局

整个项目的整体布局和分层如下:

项目所需的Maven配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>GoldenManage</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>GoldenManage</name>
    <packaging>war</packaging>
    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
        <junit.version>5.7.1</junit.version>
    </properties>
    <dependencies>
        <!--引入servlet相关依赖,https://repo.maven.apache.org/maven2/javax/servlet/servlet-api/-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!--引入jsp相关依赖,https://repo.maven.apache.org/maven2/javax/servlet/jsp/jsp-api/-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.2.1</version>
            <scope>provided</scope>
        </dependency>
        <!--引入junit测试引擎相关依赖-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/taglibs/standard -->
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.1</version>
            </plugin>
        </plugins>
    </build>
</project>

代码清单

罗列下整体项目实现的代码清单吧,其中JSP中用到了前端框架BootStrap,这个就不展开讲了,前端框架一般是在没有前端同学帮助下使用的好利器。

JSP代码清单

JSP分为如下几个页面:login.jsp用于登录,register.jsp用于注册,logout.jsp用于登出,还有两个显示结果的页面:success.jsp和error.jsp,以及显示首页的index.jsp页面

login.jsp

<%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/7/31
  Time: 16:41
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <title>用户登录</title>
    <link rel="shortcut icon" type="image/x-icon" href="images/cugbIcon.ico"/>
    <link rel="stylesheet" href="css/style.css" />
<body>
<div class="login-container">
    <h1>黄金矿工</h1>
    <div class="connect">
        <p>welcome to cugb</p>
    </div>
    <form action="ControlServlet" method="post" id="loginForm">
        <div>
            <input type="text" name="username" class="username" placeholder="用户名" autocomplete="off"/>
        </div>
        <div>
            <input type="password" name="password" class="password" placeholder="密码" oncontextmenu="return false" onpaste="return false" />
        </div>
        <button id="submit" type="submit">登 陆</button>
    </form>
    <a href="register.jsp">
        <button type="button" class="register-tis">注册</button>
    </a>
</div>
<script src="js/jquery.min.js"></script>
<script src="js/common.js"></script>
<!--背景图片自动更换-->
<script src="js/supersized.3.2.7.min.js"></script>
<script src="js/supersized-init.js"></script>
<!--表单验证-->
<script src="js/jquery.validate.min.js?var1.14.0"></script>
</body>
</html>

logout.jsp

<%--
  Created by IntelliJ IDEA.
  User: 13304
  Date: 2021/7/31
  Time: 17:22
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>退出登录</title>
  <link rel="shortcut icon" type="image/x-icon" href="images/cugbIcon.ico"/>
  <link rel="stylesheet" href="css/style.css" />
<body>
<div class="login-container">
  <h1>黄金矿工</h1>
  <div class="connect">
    <p>welcome to cugb</p>
  </div>
  <form action="StopServlet" method="get" id="loginForm">
    <button id="submit" type="submit">确认登出</button>
  </form>
</div>
<script src="js/jquery.min.js"></script>
<script src="js/common.js"></script>
<!--背景图片自动更换-->
<script src="js/supersized.3.2.7.min.js"></script>
<script src="js/supersized-init.js"></script>
<!--表单验证-->
<script src="js/jquery.validate.min.js?var1.14.0"></script>
</body>
</html>

register.jsp

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <title>用户注册</title>
    <link rel="stylesheet" href="css/style.css" />
    <link rel="shortcut icon" type="image/x-icon" href="images/cugbIcon.ico"/>
<body>
<div class="register-container">
    <h1>黄金矿工</h1>
    <div class="connect">
        <p>welcome to cugb</p>
    </div>
    <%--onpaste是否运行粘贴;oncontextmenu 是否允许展开右键菜单;placeholder 输入提示;autocomplete是否允许浏览器自动补齐--%>
    <form action="GetPermission" method="post" id="registerForm">
        <div>
            <input type="text" name="name"  placeholder="您的用户名" autocomplete="off" />
        </div>
        <div>
            <input type="password" name="password" class="password" placeholder="输入密码" oncontextmenu="return false" onpaste="return false" />
        </div>
        <div>
            <input type="password" name="confirm_password" class="confirm_password" placeholder="再次输入密码" oncontextmenu="return false" onpaste="return false" />
        </div>
        <div>
            <input type="text" name="phone_number" class="phone_number" placeholder="输入手机号码" autocomplete="off" id="number"/>
        </div>
        <div>
            <input type="email" name="email" class="email" placeholder="输入邮箱地址" oncontextmenu="return false" onpaste="return false" />
        </div>
        <button id="submit" type="submit" style="width: 332px">注 册</button>
    </form>
    <a href="login.jsp">
        <button type="button" class="register-tis" style="width: 332px">返回登录</button>
    </a>
</div>
<script src="js/jquery.min.js"></script>
<!--背景图片自动更换-->
<script src="js/supersized.3.2.7.min.js"></script>
<script src="js/supersized-init.js"></script>
<!--表单验证-->
<script src="js/jquery.validate.min.js?var1.14.0"></script>
<script src="js/common.js"></script>
</body>
</html>
相关文章
|
1月前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
23天前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
3天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
7天前
|
弹性计算 Java 关系型数据库
Web应用上云经典架构实践教学
Web应用上云经典架构实践教学
Web应用上云经典架构实践教学
|
7天前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
34 12
|
3天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
38 2
|
7天前
|
弹性计算 Java 数据库
Web应用上云经典架构实战
本课程详细介绍了Web应用上云的经典架构实战,涵盖前期准备、配置ALB、创建服务器组和监听、验证ECS公网能力、环境配置(JDK、Maven、Node、Git)、下载并运行若依框架、操作第二台ECS以及验证高可用性。通过具体步骤和命令,帮助学员快速掌握云上部署的全流程。
|
26天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
26天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
20天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####