前言
理论是实践的基础,理论指导实践。
HTTP协议是通过1个TCP套接字(管道),在浏览器和服务器之间传输字符串。
Web开发的核心技术:控制数据库数据 和 前端页面(视图)之间的动态交互,完成后台数据渲染到视图( HTML页面)。
一、三层架构
我们从接手一个项目开始,首先我们需要进行架构设计,一般我们采用的就是分层式的架构设计,即我们的三层架构。
三层架构将整个项目划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。
- 表示层(web层):与浏览器进行数据交互
- 业务层(service层): 专门用于处理业务逻辑
- 持久层(dao层): 与数据进行数据交互
三层架构的目的着重点是“高内聚,低耦合”,即解耦。
二、MVC设计模式
在确定了项目架构以后,我们再根据项目的具体需求去考虑是否需要使用一些设计模式,比如是否使用我们的MVC模式,抽象工厂模式等等。
PS:在这里我们看出,MVC和三层架构不是一个等级的,而与抽象工厂等设计模式才是一路的;
1.MVC设计模式
MVC是Model-View-Controller(模型-视图-控制器)的简称,其主要作用是将视图展示和业务控制代码分离开来。
- Model(模型): 指的就是数据或者数据的来源
- View (视图): 指的就是可视化界面
- Controller(控制器): 控制器作用于模型和视图上,负责请求的调度,它使视图与模型分离开来
2.MVVM设计模式
Vue的MVVM设计模式
- M: Model数据,对应MVC中的M
- VM:View Model,对应MVC中C,数据和视图双向绑定之后,自动完成数据----->渲染到视图。
- V:view视图,对应MVC中的V
3.三层架构和MVC设计模式的区别和联系
三层架构的目的着重点是“高内聚,低耦合”,即解耦。
MVC的目的则是实现Web系统的职能分工,即职责划分。
其实职责划分也是解耦,但是三层侧重的是项目整体解耦,而MVC侧重的是web层的解耦,即侧重jsp和Servlet的一个解耦。
三、思想指导实践
理论结合实践,了解了MVV三层架构设计思想之后,基于servlet和JPS技术,开发1个用户管理项目,在实践中深入理解设计思想。
1.Servlet
com.zhanggen.domain: 实体类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.zhanggen.domain; public class User { private String id; private String name; private String sex; private Integer age; private String address; private String qq; private String email; public User() { } public User(String id, String name, String sex, Integer age, String address, String qq, String email) { this.id = id; this.name = name; this.sex = sex; this.age = age; this.address = address; this.qq = qq; this.email = email; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getQq() { return qq; } public void setQq(String qq) { this.qq = qq; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + ", age=" + age + ", address='" + address + '\'' + ", qq='" + qq + '\'' + ", email='" + email + '\'' + '}'; } }
com.zhanggen.util: 工具类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.zhanggen.util; import com.zhanggen.domain.User; import java.io.*; import java.util.ArrayList; import java.util.List; public class DataUtils { //1. 不要放在C盘 //2. 保证这个文件是非只读的(如果下午报错中提示无权限操作这个文件,就要注意看下只读的勾是否去掉了) private static String realpath = "d:/userdata.txt"; //从文件中读取所有学员信息 public static List<User> readAll() { //保存所有学生对象信息 List<User> list = new ArrayList<>(); try { //得到文件真实路径 //创建字符输入流 Reader isr = new InputStreamReader(new FileInputStream(realpath), "UTF-8"); //创建字符缓冲流 BufferedReader br = new BufferedReader(isr); //装饰模式 //一次读一行 String row = null; while ((row = br.readLine()) != null) {//row = "1,张三,男,20" String[] arr = row.split(","); User user = new User(); user.setId(arr[0]); user.setName(arr[1]); user.setSex(arr[2]); user.setAge(Integer.parseInt(arr[3])); user.setAddress(arr[4]); user.setQq(arr[5]); user.setEmail(arr[6]); //将User对象添加到集合 list.add(user); } br.close(); } catch (Exception e) { e.printStackTrace(); } return list; } //向文件中写入所有用户信息--覆盖写 public static void writeAll(List<User> list) { try { //创建字符输出流 Writer osw = new OutputStreamWriter(new FileOutputStream(realpath), "UTF-8"); //创建字符缓冲流 BufferedWriter out = new BufferedWriter(osw); //循环向文件中写入文本 for (User user : list) { out.write(user.getId() + "," + user.getName() + "," + user.getSex() + "," + user.getAge() + "," + user.getAddress() + "," + user.getQq() + "," + user.getEmail()); out.newLine();//创建新的一行 } out.close(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { List<User> list = readAll(); System.out.println(list); } }
com.zhanggen.dao: 持久层
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.zhanggen.dao; import com.zhanggen.domain.User; import com.zhanggen.util.DataUtils; import org.apache.commons.beanutils.BeanUtils; import java.util.List; public class UserDao { public static List<User> list() { List<User> users = DataUtils.readAll(); return users; } public static void add(User user) { List<User> users = DataUtils.readAll(); users.add(user); DataUtils.writeAll(users); } public static void delete(String userID) { //1. 从txt读取到所有用户--->List List<User> userList = DataUtils.readAll(); //2. 遍历集合,找到要删除的记录 for (User user : userList) { if (userID.equals(user.getId())) { //3. 找到了要删除的记录 user userList.remove(user); break;//找到之后,跳出循环 } } //4. 将List覆盖写回到txt DataUtils.writeAll(userList); } //根据id查询 public static User findById(String id) { //1. 从txt读取到所有用户--->List List<User> userList = DataUtils.readAll(); //2. 遍历集合,根据传入的id跟遍历得到的对象的id比对 for (User user : userList) { if (id.equals(user.getId())) { //找到了目标对象,直接返回 return user; } } //3. 所有都没比对上,直接返回一个null[按照道理,是走不到这个逻辑的] return null; } //更新用户 //注意: 这个形参的位置,为了表示是修改之后的用户对象,故意起名叫newUser public static void update(User newUser) { //1. 从txt读取到所有用户--->List List<User> userList = DataUtils.readAll(); //2. 遍历集合,根据传入的user对象的id跟遍历得到的对象的id比对 for (User user : userList) { if (newUser.getId().equals(user.getId())) { //3. 找到了目标对象,就应该使用newUser对象中的属性覆盖user对象中属性 //BeanUtils.copyProperties(user,newUser): 使用参数二对象中得属性值覆盖掉参数一中对象属性的值 try { BeanUtils.copyProperties(user, newUser); } catch (Exception e) { e.printStackTrace(); } break; } } //4. 将List覆盖写回到txt DataUtils.writeAll(userList); } }
com.zhanggen.service: 业务层
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.zhanggen.service; import com.zhanggen.dao.UserDao; import com.zhanggen.domain.User; import java.util.List; public class UserService { public static List<User> list() { List<User> userlist = UserDao.list(); return userlist; } public static void add(User user) { UserDao.add(user); } //更新之前填充form表单 public static User beforeUpdate(String userID) { User currentUser = UserDao.findById(userID); return currentUser; } public static void update(User newUser) { UserDao.update(newUser); } public static void delete(String userID) { UserDao.delete(userID); } }
com.zhanggen.servlet: 表示层
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.zhanggen.servlet; import com.zhanggen.domain.User; import com.zhanggen.service.UserService; import org.apache.commons.beanutils.BeanUtils; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.List; @WebServlet("/userServlet") public class UserServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //统一设置编码 request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); String currentAction = request.getParameter("action") == null ? "空" : request.getParameter("action"); System.out.println(currentAction); switch (currentAction) { case "list": System.out.println("list"); list(request, response); break; case "add": System.out.println("add"); add(request, response); break; case "delete": System.out.println("delete"); delete(request, response); break; case "update": System.out.println("update"); update(request, response); break; default: response.getWriter().write("暂不支持" + currentAction + "方法"); } } public static void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<User> userList = UserService.list(); request.setAttribute("userList", userList); request.getRequestDispatcher("/list.jsp").forward(request, response); } public static void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User user = new User(); try { BeanUtils.populate(user, request.getParameterMap()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } UserService.add(user); //必须使用跳转不能使用转发,因为转发的时候每次都会重复添加(每次都会携带post) response.sendRedirect("/day06-user-manager/userServlet?action=list"); } public static void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String method = request.getMethod(); if ("GET".equals(method)) { String userID = request.getParameter("id"); User user = UserService.beforeUpdate(userID); request.setAttribute("user", user); request.getRequestDispatcher("/update.jsp").forward(request, response); } else if ("POST".equals(method)) { User updatedUser = new User(); try { BeanUtils.populate(updatedUser, request.getParameterMap()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } UserService.update(updatedUser); response.sendRedirect("/day06-user-manager/userServlet?action=list"); } } public static void delete(HttpServletRequest request, HttpServletResponse response) throws IOException { String userID = request.getParameter("id"); UserService.delete(userID); response.sendRedirect("/day06-user-manager/userServlet?action=list"); } }
2.JSP
主页
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html lang="zh-CN"> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>首页</title> <!-- 1. 导入CSS的全局样式 --> <link href="css/bootstrap.min.css" rel="stylesheet"> <!-- 2. jQuery导入,建议使用1.9以上的版本 --> <script src="js/jquery-2.1.0.min.js"></script> <!-- 3. 导入bootstrap的js文件 --> <script src="js/bootstrap.min.js"></script> <script type="text/javascript"> </script> </head> <body> <div align="center"> <%--<a href="/day06-user-manage/listServlet" style="text-decoration:none;font-size:33px">查询用户信息列表</a>--%> <%-- ${pageContext.request.contextPath}: 在jsp中可以使用这个EL表达式获取项目虚拟路径 --%> <a href="${pageContext.request.contextPath}/userServlet?action=list" style="text-decoration:none;font-size:33px">查询用户信息列表</a> </div> </body> </html>
数据展示列表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <!-- 网页使用的语言 --> <html lang="zh-CN"> <head> <!-- 指定字符集 --> <meta charset="utf-8"> <!-- 使用Edge最新的浏览器的渲染方式 --> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。 width: 默认宽度与设备的宽度相同 initial-scale: 初始的缩放比,为1:1 --> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>用户信息管理系统</title> <!-- 1. 导入CSS的全局样式 --> <link href="css/bootstrap.min.css" rel="stylesheet"> <!-- 2. jQuery导入,建议使用1.9以上的版本 --> <script src="js/jquery-2.1.0.min.js"></script> <!-- 3. 导入bootstrap的js文件 --> <script src="js/bootstrap.min.js"></script> <style type="text/css"> td, th { text-align: center; } </style> </head> <body> <div class="container"> <h3 style="text-align: center">用户信息列表</h3> <table border="1" class="table table-bordered table-hover"> <tr class="success"> <th>编号</th> <th>姓名</th> <th>性别</th> <th>年龄</th> <th>籍贯</th> <th>QQ</th> <th>邮箱</th> <th>操作</th> </tr> <c:forEach items="${userList}" var="user"> <tr> <td>${user.id}</td> <td>${user.name}</td> <td>${user.sex}</td> <td>${user.age}</td> <td>${user.address}</td> <td>${user.qq}</td> <td>${user.email}</td> <td> <a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/userServlet?action=update&id=${user.id}">修改</a> <a class="btn btn-default btn-sm" href="${pageContext.request.contextPath}/userServlet?action=delete&id=${user.id}">删除</a> </td> </tr> </c:forEach> <tr> <td colspan="9" align="center"> <a class="btn btn-primary" href="add.jsp">添加用户</a> </td> </tr> </table> </div> </body> </html>
添加数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!-- 网页使用的语言 --> <html lang="zh-CN"> <head> <!-- 指定字符集 --> <meta charset="utf-8"> <!-- 使用Edge最新的浏览器的渲染方式 --> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- viewport视口:网页可以根据设置的宽度自动进行适配,在浏览器的内部虚拟一个容器,容器的宽度与设备的宽度相同。 width: 默认宽度与设备的宽度相同 initial-scale: 初始的缩放比,为1:1 --> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <title>添加用户</title> <!-- 1. 导入CSS的全局样式 --> <link href="css/bootstrap.min.css" rel="stylesheet"> <!-- 2. jQuery导入,建议使用1.9以上的版本 --> <script src="js/jquery-2.1.0.min.js"></script> <!-- 3. 导入bootstrap的js文件 --> <script src="js/bootstrap.min.js"></script> </head> <body> <div class="container"> <center><h3>添加联系人页面</h3></center> <form action="${pageContext.request.contextPath}/userServlet?action=add" method="post"> <div class="form-group"> <label for="name">编号:</label> <input type="text" class="form-control" id="id" name="id" placeholder="请输入编号"> </div> <div class="form-group"> <label for="name">姓名:</label> <input type="text" class="form-control" id="name" name="name" placeholder="请输入姓名"> </div> <div class="form-group"> <label>性别:</label> <input type="radio" name="sex" value="男" checked="checked"/>男 <input type="radio" name="sex" value="女"/>女 </div> <div class="form-group"> <label for="age">年龄:</label> <input type="text" class="form-control" id="age" name="age" placeholder="请输入年龄"> </div> <div class="form-group"> <label for="address">籍贯:</label> <input type="text" class="form-control" id="address" name="address" placeholder="请输入籍贯"> </div> <div class="form-group"> <label for="qq">QQ:</label> <input type="text" class="form-control" id="qq" name="qq" placeholder="请输入QQ号码"/> </div> <div class="form-group"> <label for="email">Email:</label> <input type="text" class="form-control" id="email" name="email" placeholder="请输入邮箱地址"/> </div> <div class="form-group" style="text-align: center"> <input class="btn btn-primary" type="submit" value="提交" /> <input class="btn btn-default" type="reset" value="重置" /> <input class="btn btn-default" type="button" value="返回" /> </div> </form> </div> </body> </html>
更新数据
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<%@ page language="java" pageEncoding="UTF-8" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html> <!-- 网页使用的语言 --> <html lang="zh-CN"> <head> <!-- 指定字符集 --> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>修改用户</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <script src="js/jquery-2.1.0.min.js"></script> <script src="js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="container"> <h3 style="text-align: center;">修改用户</h3> <form action="${pageContext.request.contextPath}/userServlet?action=update" method="post"> <div class="form-group"> <label for="name">编号:</label> <input type="text" class="form-control" id="id" readonly="readonly" name="id" value="${user.id}"> </div> <div class="form-group"> <label for="name">姓名:</label> <input type="text" class="form-control" id="name" name="name" value="${user.name}" placeholder="请输入姓名"/> </div> <div class="form-group"> <label>性别:</label> <input type="radio" name="sex" value="男" <c:if test="${user.sex == '男'}">checked</c:if> />男 <input type="radio" name="sex" value="女" <c:if test="${user.sex == '女'}">checked</c:if> />女 </div> <div class="form-group"> <label for="age">年龄:</label> <input type="text" class="form-control" id="age" name="age" placeholder="请输入年龄" value="${user.age}"/> </div> <div class="form-group"> <div class="form-group"> <label for="address">籍贯:</label> <input type="text" class="form-control" id="address" name="address" placeholder="请输入籍贯" value="${user.address}"> </div> </div> <div class="form-group"> <label for="qq">QQ:</label> <input type="text" class="form-control" id="qq" name="qq" placeholder="请输入QQ号码" value="${user.qq}"/> </div> <div class="form-group"> <label for="email">Email:</label> <input type="text" class="form-control" id="email" name="email" placeholder="请输入邮箱地址" value="${user.email}"/> </div> <div class="form-group" style="text-align: center"> <input class="btn btn-primary" type="submit" value="提交"/> <input class="btn btn-default" type="reset" value="重置"/> <input class="btn btn-default" type="button" value="返回"/> </div> </form> </div> </div> </body> </html>
3.数据源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1,jack,男,28,香港,123456,123456@qq.com 2,lucy,男,38,香港,223456,223456@qq.com 3,rookie,女,48,北京,66779921,fxg@163.com 4,james,男,48,北京,1233212,guodegang@163.com 5,张根,男,29,河北,645172205,13220198866@163.com