MVC设计模式
MVC理解图
概念 - 代码的分层
字母 | 表示 | 层 | 理解 |
---|---|---|---|
M | Modle | 模型层 | 业务的具体实现 |
V | View | 视图层 | 展示数据 |
C | Controller | 控制器层 | 控制业务流程 |
细化理解层数
View:视图层,用于存放前端页面
Controller:控制器层,用于存放Servlet
Modle-Biz:逻辑业务层,用于存放业务具体的实现
Modle-Dao:数据持久层,用于存放操作数据的实现
优缺点
缺点:使用MVC不能减少代码量, 增加系统结构和实现的复杂性
优点:整个项目结构清晰,业务逻辑清晰,降低了代码的耦合性,代码的重用性高
各层的命名规范
Controller控制器层:controller/servlet/action/web
Modle-Biz 逻辑业务层:service/biz
Modle-Dao 数据持久层:dao/persist/mapper
MVC学生管理系统
Mapper层
操作数据库
接口
CourseMapper
TeacherMapper
StudentMapper
java
package com.ckl.mapper;
import com.ckl.pojo.Student;
import java.util.List;
public interface StudentMapper {
public void add(String username,String password,String name,String sex,int age,String hobbies,String photo);
public void delete(String username);
public void update(String username,String newPassword);
public void update(String username,String name,String sex,int age,String hobbies);
public void update(String username,String name,String sex,int age,String hobbies,String photo);
public Student getStudent(String username);
public Student getStudent(String username,String password);
public List<Student> getStudents(int offset,int count);
public int getAllCount();
}
实现类
CourseMapperImpl
TeacherMapperImpl
StudentMapperImpl
java
package com.ckl.mapper.impl;
import com.ckl.mapper.StudentMapper;
import com.ckl.pojo.Student;
import com.ckl.utils.DBUtils;
import java.sql.SQLException;
import java.util.List;
public class StudentMapperImpl implements StudentMapper {
@Override
public void add(String username, String password, String name, String sex, int age, String hobbies, String photo) {
try {
DBUtils.commonUpdate("insert into student(username,password,name,sex,age,hobbies,photo) values(?,?,?,?,?,?,?)",username,password,name,sex,age, hobbies,photo);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void delete(String username) {
try {
DBUtils.commonUpdate("delete from student where username = ?",username);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void update(String username, String newPassword) {
try {
DBUtils.commonUpdate("update student set password=? where username=?",newPassword,username);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void update(String username, String name, String sex, int age, String hobbies) {
try {
DBUtils.commonUpdate("update student set name=?,sex=?,age=?,hobbies=? where username=?",name,sex,age,hobbies,username);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void update(String username, String name, String sex, int age, String hobbies, String photo) {
try {
DBUtils.commonUpdate("update student set name=?,sex=?,age=?,hobbies=?,photo=? where username=?",name,sex,age,hobbies,photo,username);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Student getStudent(String username) {
Student student = null;
try {
student = DBUtils.commonQueryObj(Student.class, "select * from student where username = ?", username);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return student;
}
@Override
public Student getStudent(String username, String password) {
Student student = null;
try {
student = DBUtils.commonQueryObj(Student.class, "select * from student where username = ? and password = ?", username, password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return student;
}
@Override
public List<Student> getStudents(int offset, int count) {
List<Student> students = null;
try {
students = DBUtils.commonQueryList(Student.class, "select * from student limit ?,?", offset, count);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return students;
}
@Override
public int getAllCount() {
int allcount = 0;
try {
allcount = DBUtils.getAllCount("student");
} catch (SQLException e) {
throw new RuntimeException(e);
}
return allcount;
}
}
Service层
业务功能实现
接口
对于后续管理员的功能,会用到的Service方法先在Service接口规范出来
CourseService
是否添加、添加、修改课程信息初始化、修改课程信息、查询课程、删除
UserService
验证码、登录、记住我【直接传需要的数据】、安全退出、修改密码
TeacherService
是否添加、添加、修改课程信息初始化、修改课程信息、查询课程、删除
StudentService
是否注册、注册、修改课程信息初始化、修改课程信息、查询课程、删除
java
package com.ckl.service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface StudentService {
public boolean isRegister(String username);
public boolean register(HttpServletRequest request, HttpServletResponse response);
public void initModify(HttpServletRequest request, HttpServletResponse response);
public void modify(HttpServletRequest request, HttpServletResponse response);
public void getStudents(HttpServletRequest request, HttpServletResponse response);
public void delete(HttpServletRequest request, HttpServletResponse response);
}
实现类
【一般传请求和响应】
1.不做跳转
2.涉及数据库的操作都求助于mapper层,newmapperImpl层实现类对象,再使用多态向上转型mapper对象进行方法调用
private StudentMapper studentMapper = new StudentMapperImpl();
3.对于学生老师在添加时,会利用到对象和输入输出流就会需要写一个解析请求数据包类使用泛型【新建一个core包】,解析请求的工具类【处理文本数据和二进制数据】,利用反射
public class ParseRequestData<T> {
private T t;
private InputStream in;
private OutputStream out;
...
}
4.对于学生老师信息的修改,会出现不修改头像的情况,就需要在mapper层添加方法;在处理业务,工具类要注意判断输入流,有就改了没有就不改,注意流的关闭
public void update(String username,String name,String sex,int age,String hobbies);
public void update(String username,String name,String sex,int age,String hobbies,String photo);
存的头像路径就会在service层传路径,传入在工具类处理就会自定义新的路径,再存就可以
CourseServiceImpl
UserServiceImpl
对于角色判断登录使用多态获取user对象,直接调用Mapper层方法,记住我也是调用同类方法,登录成功失败都只是返回boolean值
TeacherServiceImpl
除了获取TeacherMapper对象,还要用到CourseService对象
private TeacherMapper teacherMapper = new TeacherMapperImpl();
private CourseService courseService = new CourseServiceImpl();
StudentServiceImpl
注意:查询涉及分页,就新新增工具类PageUtils,获取总页数;
java
package com.ckl.service.impl;
import com.ckl.core.ParseRequestData;
import com.ckl.dto.StudentDto;
import com.ckl.mapper.StudentMapper;
import com.ckl.mapper.impl.StudentMapperImpl;
import com.ckl.pojo.Page;
import com.ckl.pojo.Student;
import com.ckl.service.StudentService;
import com.ckl.utils.*;
import org.apache.commons.io.IOUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public class StudentServiceImpl implements StudentService {
private StudentMapper studentMapper = new StudentMapperImpl();
@Override
public boolean isRegister(String username) {
Student student = studentMapper.getStudent(username);
if(student == null){
return true;
}
return false;
}
@Override
public boolean register(HttpServletRequest request, HttpServletResponse response) {
ParseRequestData<Student> parseRequestData = ParseRequestDataUtils.parseRequest(request, Student.class, "upload\\student");
Student stu = parseRequestData.getT();
InputStream in = parseRequestData.getIn();
OutputStream out = parseRequestData.getOut();
boolean register = isRegister(stu.getUsername());
if(register){
//将数据插入到学生表中
studentMapper.add(stu.getUsername(),stu.getPassword(),stu.getName(),stu.getSex(),stu.getAge(),stu.getHobbies(),stu.getPhoto());
//将头像存储到本地磁盘
try {
IOUtils.copy(in,out);
in.close();
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return true;
}else{
try {
in.close();
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
request.setAttribute("msg","注册失败 -- 账号已注册");
return false;
}
}
@Override
public void initModify(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
Student student = studentMapper.getStudent(username);
request.setAttribute("student",student);
}
@Override
public void modify(HttpServletRequest request, HttpServletResponse response) {
ParseRequestData<Student> parseRequestData = ParseRequestDataUtils.parseRequest(request, Student.class, "upload\\student");
Student stu = parseRequestData.getT();
InputStream in = parseRequestData.getIn();
OutputStream out = parseRequestData.getOut();
try {
if(stu.getPhoto() != null){//说明用户修改头像
studentMapper.update(stu.getUsername(),stu.getName(), stu.getSex(), stu.getAge(),stu.getHobbies(),stu.getPhoto());
IOUtils.copy(in,out);
}else{
studentMapper.update(stu.getUsername(),stu.getName(), stu.getSex(), stu.getAge(),stu.getHobbies());
}
if(in != null){
in.close();
}
if(out != null){
out.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
String role = (String) request.getSession().getAttribute("role");
if("student".equals(role)){
//更新Session里的数据
request.getSession().setAttribute("name",stu.getName());
//更新Cookie里的数据
response.addCookie(CookieUtils.createCookie("name",stu.getName(),60*60*24*5));
if(stu.getPhoto() != null){
request.getSession().setAttribute("photo",stu.getPhoto());
response.addCookie(CookieUtils.createCookie("photo",stu.getPhoto(),60*60*24*5));
}
}
}
@Override
public void getStudents(HttpServletRequest request, HttpServletResponse response) {
//获取当前页数
int curPage = Integer.parseInt(request.getParameter("curPage"));
//设置URL
String url = "student?action=doGetStudents&curPage=";
//设置当前页的数据条数
int count = 15;
//计算偏移量
int offset = (curPage-1)*count;
//计算总页数
int allCount = studentMapper.getAllCount();
int totalPage = PageUtils.getTotalPage(allCount,count);
//从数据库获取学生的集合
List<Student> students = studentMapper.getStudents(offset,count);
//处理学生集合
List<StudentDto> studentDtos = DtoUtils.studentDtoListHandler(students);
//封装Page对象
Page<StudentDto> page = new Page<>(url, curPage, totalPage, studentDtos);
//将数据存入到请求对象中
request.setAttribute("page",page);
}
@Override
public void delete(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
studentMapper.delete(username);
}
}
Controller层
做跳转
前面的项目用的Servlet,一个功能一个Servlet会很多
现在分角色来写Servlet,不同角色里有对应的多个功能
减少了多个Servlet堆积
实现原理
客户端发送的请求都会找到对应角色的Controller层,但是角色中包含很多功能,需要判断功能,如何实现?
理解:表单提交不是用到action,同理我们判断对应功能,就在jsp对应功能写上角色名+对应功能的action
角色名即找到对应的ControllerServlet
Controller里,获取action,进行功能判断
ControllerServlet
CourseController
UserController
TeacherController
StudentController
@WebServlet("/student")
public class StudentController extends HttpServlet {
private StudentService studentService = new StudentServiceImpl();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getParameter("action");
if("doRegister".equals(action)){
boolean register = studentService.register(request, response);
if(register){
response.sendRedirect("login.jsp");
}else{
request.getRequestDispatcher("register.jsp").forward(request,response);
}
}else if("doInitModify".equals(action)){
studentService.initModify(request,response);
request.getRequestDispatcher("stuInfo.jsp").forward(request,response);
}else if("doModify".equals(action)){
studentService.modify(request,response);
String role = (String) request.getSession().getAttribute("role");
if("student".equals(role)){
response.sendRedirect("index.jsp");
}else if("teacher".equals(role)){
request.getRequestDispatcher("student?action=doGetStudents&curPage=1").forward(request,response);
}
}else if("doGetStudents".equals(action)){
studentService.getStudents(request,response);
request.getRequestDispatcher("stuList.jsp").forward(request,response);
}else if("doDelete".equals(action)){
studentService.delete(request,response);
request.getRequestDispatcher("student?action=doGetStudents&curPage=1").forward(request,response);
}
}
}
功能判断处理方式
法一:extends HttpServlet,就会实现doGet()、doPost(),在doPost()里获取请求的action,进行if-else判断功能
法二:extends BaseServlet,写一个用来判断功能的BaseServlet,在BaseServlet也就是获取action,不同是利用反射进行获取调用对应功能方法,这样就不用if-else判断,而在ControllerServlet里就是一个个方法,可维护性更高
java
package com.qf.servlet;
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.lang.reflect.Method;
public class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//url -- http://localhost:8080/Day24_MVC_war_exploded/user?action=doLogin
String action = request.getParameter("action");//doLogin
//获取Controller类的class对象
Class<? extends BaseServlet> clazz = this.getClass();
try {
//根据action获取Controller类对应的方法对象
Method method = clazz.getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
//设置操作权限
method.setAccessible(true);
//调用方法
method.invoke(this,request,response);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
java
package com.ckl.controller;
import com.ckl.service.StudentService;
import com.ckl.service.impl.StudentServiceImpl;
import com.ckl.servlet.BaseServlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/student")
public class StudentController extends BaseServlet {
private StudentService studentService = new StudentServiceImpl();
public void doRegister(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
boolean register = studentService.register(request, response);
if(register){
response.sendRedirect("login.jsp");
}else{
request.getRequestDispatcher("register.jsp").forward(request,response);
}
}
public void doInitModify(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
studentService.initModify(request,response);
request.getRequestDispatcher("stuInfo.jsp").forward(request,response);
}
public void doModify(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
studentService.modify(request,response);
String role = (String) request.getSession().getAttribute("role");
if("student".equals(role)){
response.sendRedirect("index.jsp");
}else if("teacher".equals(role)){
request.getRequestDispatcher("student?action=doGetStudents&curPage=1").forward(request,response);
}
}
public void doGetStudents(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
studentService.getStudents(request,response);
request.getRequestDispatcher("stuList.jsp").forward(request,response);
}
public void delete(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
studentService.delete(request,response);
request.getRequestDispatcher("student?action=doGetStudents&curPage=1").forward(request,response);
}
}
View
页面数据展示,功能匹配ControllerServlet和其中的方法来陆续实现
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%@ include file="rememberMe.jsp"%>
${msg}
<h1>登录页面</h1>
<form action="user?action=doLogin" method="post">
账号:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
验证码:<input type="text" name="userCode"/><img src="user?action=doDrawCode" width="120px" height="30px" οnclick="refresh()"><a href="#" οnclick="refresh()">刷新</a><br/>
记住我:<input type="checkbox" name="rememberMe"/><br/>
角色:
<select name="role">
<option value="student">学生</option>
<option value="teacher">老师</option>
</select>
<br/>
<input type="submit" value="登录"/>
<input type="button" value="返回" οnclick="goWelcome()"/>
</form>
<script type="text/javascript">
function goWelcome(){
window.location = "welcome.html";
}
img = document.getElementsByTagName("img")[0];
function refresh(){
img.src = "user?action=doDrawCode&" + new Date();
}
</script>
</body>
</html>