前面的十一篇博客把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就把这部分补充上,项目名称为【黄金找矿网站】。
项目要求
在正式做之前先确定一下项目的要求,要求完成注册登录任务,任务需要满足如下几个要点:
- 未登录之前,除了注册登录页面,站点的其它页面均不能访问,请求时都自动跳转到登录页面
- 注册用户信息时,需要有前后端验证,对用户提交的表单进行表单验证
- 表单验证失败后收集错误信息跳转到错误页面,告知失败原因
- 注册成功后跳转到注册成功页面,告知用户的注册信息
- 登录时判断库里是否有该用户,只有存在且密码正确才可以登录成功
- 点击登出后当前用户恢复未登录状态
- 站点开启和关闭时需要加监控,监控服务的运行状态
其实这样一个注册登录项目就是我这两天设计出来用来将历史十一篇blog学习过的技术进行整合的,每个要求都涉及相关知识点。
技术要点
对照上边的实现要求,其实可以能抽象出所有的技术要点:
- 未登录之前,除了注册登录页面,站点的其它页面均不能访问,请求时都自动跳转到登录页面【Filter过滤器&重定向&Session作用域】
- 注册用户信息时,需要有前后端验证,对用户提交的表单进行表单验证【表单提交&表单验证&请求转发&Servlet常用对象】
- 表单验证失败后收集错误信息跳转到错误页面,告知失败原因【JSTL标签】
- 注册成功后跳转到注册成功页面,告知用户的注册信息【JSP语法&EL表达式&JavaBean】
- 登录时判断库里是否有该用户,只有存在且密码正确才可以登录成功【JDBC】
- 点击登出后当前用户恢复未登录状态【Session销毁】
- 站点开启和关闭时需要加监控,监控服务的运行状态【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>