2.MVC
注册登录案例,数据保存在json文件中。
还想在新的Moudle中导入Gson的包,在同一project的另一个Moudle中已经导入过Gson的包了,就不需要重新add as library了。
可以右击Moudle—open Moudle settings
然后点击右侧的library
Add select即可
最后注册的新的账号密码会在部署目录下的user.json中找到,在开发目录中的user.json是不会有新账号添加进去的。
代码主要逻辑:
RegisterServlet:
package com.cskaoyan;
import com.cskaoyan.bean.User;
import com.cskaoyan.utils.StringUtils;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import javax.imageio.spi.RegisterableService;
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.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author shihao
* @create 2020-07-02 10:15
*/
@WebServlet("/register")
public class registerServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//注册,注册的信息保存在json文件中
//取出参数,判断用户名是否一致
String username = request.getParameter("username");
String password = request.getParameter("password");
String confirmPassword = request.getParameter("confirmPassword");
//校验,是否为空,密码与校验密码是否为空
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password) || StringUtils.isEmpty(confirmPassword)) {
response.getWriter().println("参数不能为空!");
return;
}
if (!password.equals(confirmPassword)) {
response.getWriter().println("两次密码不一致,请确认");
return;
}
//封装对象
User user = new User();
user.setUsername(username);
user.setPassword(password);
//json文件中是否存在这个用户名,不存在才注册
//读取user.json文件
InputStream inputStream = registerServlet.class.getClassLoader().getResourceAsStream("user.json");
//形成一个字符串就行了
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len;
//把整个流信息读取到byte Array中
while ((len = inputStream.read(bytes)) != -1) {
byteOutputStream.write(bytes, 0, len);
}
String jsonStr = byteOutputStream.toString("utf-8");
//将jsonStr转换成json对象
Gson gson = new Gson();
List<User> users = new ArrayList<>();
if (!"".equals(jsonStr)) {
JsonElement jsonElement = new JsonParser().parse(jsonStr);
JsonArray jsonArray = jsonElement.getAsJsonArray();
for (JsonElement element : jsonArray) {
User u = gson.fromJson(element, User.class);
//判断是否存在当前用户名
if (u.getUsername().equals(username)) {
response.getWriter().println("当前用户名已经存在");
return;
}
//将user.json文件中的所有数据都写出到users这个list中
users.add(u);
}
}
//在list中再加入新的user对象
users.add(user);
//注册完毕,把users这个list中的数据写回到user.json文件中
String s = gson.toJson(users);
String file = registerServlet.class.getClassLoader().getResource("user.json").getFile();
FileOutputStream fos = new FileOutputStream(file);
fos.write(s.getBytes("utf-8"));
response.getWriter().println("注册成功,即将跳转至登录页面...");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
如果这个时候业务需求变更,不再要求使用json来操作,而是要求使用数据库,整体的一个功能不变,只是更改技术的实现形式。下面所有的代码都需要全部替换,但是框内标注出来的需要保留下来。
但我们发现这段代码的耦合度非常高,这就引入了MVC的概念。
user对象属于model,对user对象的所有操作:比如注册、判断用户名是否存在、登录,这些都是对于user对象的操作。也算model。
View:对数据模型的显示。用户个人页面:显示的就是user的信息。Jsp。
Controller:中转站。中间人。请求到来以后,控制器做一些处理,比如校验,之后调用model的一个或者多个方法,接下来model返回结果,根据结果的不同,调用不同的视图。model和view之间就不会产生任何的影响。
此时耦合程度相较于之前减少了很多。需要将数据保存到mysql里。
从json迁移到mysql,只需要将controller里面这两行代码做一个更改,其他完全不用变。
只要原先的json实现和mysql实现,他们返回的结果都是同样的含义,即true就表示存在,false就表示不存在。那么视图层的代码完全不会受到任何影响。
是不是相当于制定了一个规范、标准???????
哪个天生就是一个标准???????接口。
2.1.三层架构
接下来,需要将MVC进一步优化,这里面其实还有很大可优化的空间?如果方法很多,需要将每个方法里面的之前的model全部换成新的model,更改的地方还是很多。
仔细地去体会一下,为什么仅需要去修改类名,而不需要去修改方法名称以及返回值,因为我们是刻意这么做的,因为这样做的话修改的地方最少。潜意识里去做一个标准。
为何不用天生就适合做标准的呢?????????接口。
使用接口,不管是Json还是Mysql,其实都是在操作数据,又可以给它们重新取一个名字,叫做DAO,Data Access Object。
三层架构,究竟是哪三层呢?
说法很多,展示层、业务层service、数据层dao
Controller、service、dao
有一个疑惑:
感觉有controller和dao已经可以实现功能了,为什么还需要有service呢?
对于一些逻辑非常简单的模块,没有service感觉没有任何影响,但是
如果对于一些业务逻辑比较复杂的模块,service就体现出它的功能了。
比如分页查询数据结果。
分页显示图中数据。
一共多少条记录,每页多少条,一共多少页,同时展示当前页应该显示的数据条目。
Map map = userDao.queryPageUsers(currentPage);
map里面应当包含总记录数、每页数据、总页数、当前页里面的条目
所以此时dao里面的代码需要做很多的逻辑,发起两次查询,count page数据
如果dao里面的逻辑非常多,那么它的可复用性就会很差
比如在其他地方,只需要查询总记录数,那么就会显得很不方便。
每次查询数据库应当尽可能是单一的,这样可复用性就会很高。
组织在一起的话,就在service层里面做。
Request.setAttribute(map, map);
转发给视图
比较好的代码应当是这样的:
Controller:
不是直接调用dao层,而是调用service
Map map = userService.queryPageUsers(currentPage)
service层中:发起一个一个的dao调用
queryPageUsers方法内:
调用dao层的多个方法:
Total = userDao.totalCount()
List = userDao.currentPageUser(currentPage);
totalPage = total/page_size
真的不知道怎么debug的话就在doPost第一行代码中打一个断点,和在doGet中打一个断点。因为这是servlet的执行入口
Controller:控制器;做一些校验;调用service返回结果,根据结果调用不同的视图
Service和Dao其实就相当于之前的model
Service:业务;每个功能具体的业务逻辑应当写在service,每个功能区别于其他功能这些独特的代码应当写在service。比如分页显示数据。事务。
Dao:发起一个一个的sql语句。一个dao方法里面最好不要多次sql操作,否则它的可复用性会很差。
此时,不同模块之间的耦合点在哪?
controller和service的耦合点
service和dao的耦合点
此时的耦合程度算高还是低呢?低。那能不能更低呢?
耦合度再低一点就是红线部分的删除掉,这样controller中存的仅仅是service接口的一个引用,并没有主动去给其赋值,给他赋值可能会是其他容器在某一阶段给赋的值。
这其实就是Spring的思想。
SpringMVC:作用于controller、view
Spring:不是作用于service。一整套的解决方案,将各个部分进行解耦与整合。
Mybatis:作用于dao