1.思维导图:
?
2.解决框架配置文件名及放置问题:
?2.1 中央控制器(DispatchServlet)代码:
? ?如果web.xml没有配置就默认自己写的文件名为准
package com.leijiajia.framework;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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 org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import com.leijiajia.web.GoodsAction;
/**
* 目标:
* 根据自定义MVC框架的原理图完成 框架的研发
*
* @author zjjt
*
* 中央控制器
* 目标:寻找子控制器
*
*/
/*@WebServlet("*.action")*/
public class DispatchServlet extends HttpServlet {
//专门存放子控制器的容器
//private Map<String, ActionSupport> actions=new HashMap<String, ActionSupport>();
private ConfigModel configModel = null;
//初始化子控制器容器(集合),经过初始化,actions容器内部就有了子控制器
//init,service,destroy
/**
* 需求:
* 在增加一个商品类的增删改查
* 步骤:
* 改动init中代码
* 思考:
* 能不能不改动代码完成这个需求
* 1.参考DBAcess的数据源配置文件config.prooerties
* 1.1 减少代码改动风险性
* 1.2 减少代码的编译次数(对于已经部署到服务器后)
* 解决方案:
* 改成子控制器可配置
* 解决步骤:
* 1.必须有配置文件config.xml
* 2.配置文件config.xml中要包含处理业务的子控制器
* 3.读取到配置文件config.xml中的对应的处理浏览器请求的子控制器
* 编码:
* ...
*/
@Override
public void init() throws ServletException {
//缺陷:需要改动原有代码,伴随着很大的风险
try {
//配置的位置:web.xml
String configurationLocation = this.getInitParameter("configurationLocation");
if(configurationLocation==null || "".equals(configurationLocation)) {
configurationLocation="/zking.xml";
}
configModel=ConfigModelFactory.build(configurationLocation);
} catch (Exception e) {
e.printStackTrace();
}
// actions.put("/book", new BookAction());
// actions.put("/goods", new GoodsAction());
// actions.put("/OrderItme", new BookAction());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//完成寻找子控制器的过程
//浏览器:http://localhost:8080/t266_mvc/book.action?methodName=add
//目标:BookAction.add()...
/**
* 思路:
* 1.从浏览器URL中获取到“book”字符串
* 2.在子控制器容器中拿到BookAction
* 3.BookAction.add()
*/
/**
* Debug的使用效果:
* 1.调试代码(找错)
* 2.观看\观察代码的一个流程走向,每一个参数的值
*/
String uri = req.getRequestURI();
uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));
//System.out.println(uri);
//action=BookAction
//ActionSupport action = actions.get(uri);
ActionModel actionModel = configModel.pop(uri);
String type = actionModel.getType();
ActionSupport action;
try {
//放射实例化
action = (ActionSupport) Class.forName(type).newInstance();
//ActionSupport action=new BookAction();
if(action instanceof ModelDriver) {
ModelDriver m=(ModelDriver) action;
Object obj = m.getModel();
//有对象
//接收所有的前端jsp传递到后台的参数
Map<String, String[]> parameterMap = req.getParameterMap();
//给对象赋值
//PropertyUtils.getProperty(obj, "");
BeanUtils.populate(obj, parameterMap);
}
//execute->delete
String res = action.execute(req, resp);
/**
* 思路:
* 1.方法执行宪法完毕必须有一个返回值
* 2.通过返回值决定是否重定向还是转发
* 3.通过返回值决定跳转那一页
*/
ForwardModel forwardModel = actionModel.pop(res);
if(forwardModel.isRedirect()) {
//重定向
resp.sendRedirect(req.getContextPath()+forwardModel.getPath());
}
else {
//转发
req.getRequestDispatcher(forwardModel.getPath()).forward(req, resp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 运行结果:
?
3.通用的增删改:
3.1 BaseDao代码:
package com.leijiajia.util;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.leijiajia.util.DBAccess;
import com.leijiajia.util.PageBean;
import com.leijiajia.util.StringUtils;
/**
* 所有Dao层的父类
*
* @param <T>
*/
public class BaseDao<T> {
/**
* 通用的增删改
* @param sql 具体的增删改SQL
* @param t 具体要操作的表对应实体类
* @param attrs 具体操作表的某些字段对应的实体类属性
* @throws Exception
*/
public void executeUpdate(String sql, T t, String[] attrs) throws Exception {
//?->sid;?->sname;?->remark;
//attrs = new String[] {"sid","sname","remark"};
Connection con = DBAccess.getConnection();
PreparedStatement pst = con.prepareStatement(sql);
/*pst.setObject(1, student.getSid());
pst.setObject(2, student.getSname());
pst.setObject(3, student.getRemark());*/
//遍历
for (int i = 0; i < attrs.length; i++) {
//attrs[0]=sid;attrs[1]=sname;
Field f = t.getClass().getDeclaredField(attrs[i]);
f.setAccessible(true);
//f.get(t); sid.get(student)
pst.setObject(i+1, f.get(t));
}
pst.executeUpdate();
}
/**
* 通用分页查询
* @param sql
* @param clz
* @return
* @throws Exception
*/
public List<T> executeQuery(String sql,Class<T> clz,PageBean pageBean) throws Exception{
List<T> list = new ArrayList<T>();
Connection con = DBAccess.getConnection();;
PreparedStatement pst = null;
ResultSet rs = null;
/*
* 是否需要分页?
* 无需分页(项目中的下拉框,查询条件教员下拉框,无须分页)
* 必须分页(项目中列表类需求、订单列表、商品列表、学生列表...)
*/
if(pageBean != null && pageBean.isPagination()) {
// 必须分页(列表需求)
String countSQL = getCountSQL(sql);
pst = con.prepareStatement(countSQL);
rs = pst.executeQuery();
if(rs.next()) {
pageBean.setTotal(String.valueOf(rs.getObject(1)));
}
// 挪动到下面,是因为最后才处理返回的结果集
// -- pageSql=sql limit (page-1)*rows,rows 对应某一页的数据
// -- countSql=select count(1) from (sql) t 符合条件的总记录数
String pageSQL = getPageSQL(sql,pageBean);//符合条件的某一页数据
pst = con.prepareStatement(pageSQL);
rs = pst.executeQuery();
}else {
// 不分页(select需求)
pst = con.prepareStatement(sql);//符合条件的所有数据
rs = pst.executeQuery();
}
while (rs.next()) {
T t = clz.newInstance();
Field[] fields = clz.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
f.set(t, rs.getObject(f.getName()));
}
list.add(t);
}
return list;
}
/**
* 将原生SQL转换成符合条件的总记录数countSQL
* @param sql
* @return
*/
private String getCountSQL(String sql) {
// -- countSql=select count(1) from (sql) t 符合条件的总记录数
return "select count(1) from ("+sql+") t";
}
/**
* 将原生SQL转换成pageSQL
* @param sql
* @param pageBean
* @return
*/
private String getPageSQL(String sql,PageBean pageBean) {
// (this.page - 1) * this.rows
// pageSql=sql limit (page-1)*rows,rows
return sql + " limit "+ pageBean.getStartIndex() +","+pageBean.getRows();
}
}
4.增删改的Dao层实现:
4.1 StudentDao(方法类)代码:
4.2 运行结果:
?
5.增删改查代码完整实现:
?5.1 BaseDao代码:
package com.leijiajia.util;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.leijiajia.util.DBAccess;
import com.leijiajia.util.PageBean;
import com.leijiajia.util.StringUtils;
/**
* 所有Dao层的父类
*
* @param <T>
*/
public class BaseDao<T> {
/**
* 通用的增删改
* @param sql 具体的增删改SQL
* @param t 具体要操作的表对应实体类
* @param attrs 具体操作表的某些字段对应的实体类属性
* @throws Exception
*/
public void executeUpdate(String sql, T t, String[] attrs) throws Exception {
//?->sid;?->sname;?->remark;
//attrs = new String[] {"sid","sname","remark"};
Connection con = DBAccess.getConnection();
PreparedStatement pst = con.prepareStatement(sql);
/*pst.setObject(1, student.getSid());
pst.setObject(2, student.getSname());
pst.setObject(3, student.getRemark());*/
//遍历
for (int i = 0; i < attrs.length; i++) {
//attrs[0]=sid;attrs[1]=sname;
Field f = t.getClass().getDeclaredField(attrs[i]);
f.setAccessible(true);
//f.get(t); sid.get(student)
pst.setObject(i+1, f.get(t));
}
pst.executeUpdate();
}
/**
* 通用分页查询
* @param sql
* @param clz
* @return
* @throws Exception
*/
public List<T> executeQuery(String sql,Class<T> clz,PageBean pageBean) throws Exception{
List<T> list = new ArrayList<T>();
Connection con = DBAccess.getConnection();;
PreparedStatement pst = null;
ResultSet rs = null;
/*
* 是否需要分页?
* 无需分页(项目中的下拉框,查询条件教员下拉框,无须分页)
* 必须分页(项目中列表类需求、订单列表、商品列表、学生列表...)
*/
if(pageBean != null && pageBean.isPagination()) {
// 必须分页(列表需求)
String countSQL = getCountSQL(sql);
pst = con.prepareStatement(countSQL);
rs = pst.executeQuery();
if(rs.next()) {
pageBean.setTotal(String.valueOf(rs.getObject(1)));
}
// 挪动到下面,是因为最后才处理返回的结果集
// -- pageSql=sql limit (page-1)*rows,rows 对应某一页的数据
// -- countSql=select count(1) from (sql) t 符合条件的总记录数
String pageSQL = getPageSQL(sql,pageBean);//符合条件的某一页数据
pst = con.prepareStatement(pageSQL);
rs = pst.executeQuery();
}else {
// 不分页(select需求)
pst = con.prepareStatement(sql);//符合条件的所有数据
rs = pst.executeQuery();
}
while (rs.next()) {
T t = clz.newInstance();
Field[] fields = clz.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
f.set(t, rs.getObject(f.getName()));
}
list.add(t);
}
return list;
}
/**
* 将原生SQL转换成符合条件的总记录数countSQL
* @param sql
* @return
*/
private String getCountSQL(String sql) {
// -- countSql=select count(1) from (sql) t 符合条件的总记录数
return "select count(1) from ("+sql+") t";
}
/**
* 将原生SQL转换成pageSQL
* @param sql
* @param pageBean
* @return
*/
private String getPageSQL(String sql,PageBean pageBean) {
// (this.page - 1) * this.rows
// pageSql=sql limit (page-1)*rows,rows
return sql + " limit "+ pageBean.getStartIndex() +","+pageBean.getRows();
}
}
5.2 StudentAction 代码(子控制器):?
package com.leijiajia.web;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.leijiajia.dao.StudentDao;
import com.leijiajia.entity.Student;
import com.leijiajia.framework.ActionSupport;
import com.leijiajia.framework.ModelDriver;
import com.leijiajia.util.PageBean;
/**
* 子控制器
* @author zjjt
*
*/
public class StudentAction extends ActionSupport implements ModelDriver<Student>{
//操作的实体类定义在外面
private Student student=new Student();
private StudentDao studentDao=new StudentDao();
@Override
public Student getModel() {
return student;
}
public String add(HttpServletRequest req, HttpServletResponse resp) {
try {
studentDao.add(student);
} catch (Exception e) {
e.printStackTrace();
}
return "toList";
}
public String delete(HttpServletRequest req, HttpServletResponse resp) {
try {
studentDao.delete(student);
} catch (Exception e) {
e.printStackTrace();
}
return "toList";
}
public String edit(HttpServletRequest req, HttpServletResponse resp) {
try {
studentDao.edit(student);;
} catch (Exception e) {
e.printStackTrace();
}
return "toList";
}
public String list(HttpServletRequest req, HttpServletResponse resp) {
PageBean pageBean=new PageBean();
pageBean.setRequest(req);
try {
List<Student> list = studentDao.list(student, pageBean);
req.setAttribute("students", list);
req.setAttribute("pageBean", pageBean);
} catch (Exception e) {
e.printStackTrace();
}
return "list";
}
public String toEdit(HttpServletRequest req, HttpServletResponse resp) {
//如果跳转的新增界面 无需查询,如果跳转的是修改界面,需要查询当前sid对应的数据,回显到界面
if(student.getSid()!=0) {//修改
try {
List<Student> list = studentDao.list(student, null);//根据id查询,有且只有一条数据
req.setAttribute("b", list.get(0));
} catch (Exception e) {
e.printStackTrace();
}
}
return "toEdit";
}
}
?
5.2 运行结果:
?
6.编写代码过程的报错:
6.1 图片截图:
?
6.2 报错原因:
?mvc.xml需改成configurationLocation
?6.3 报错截图:
?6.4 报错原因:
? ? ?驱动包导错了
?6.5 报错截图:
?6.6 报错原因:
需要把con.zking.tag.PageTag该为自己包路径
?
?6.6 报错截图:
??6.7 报错原因:
?
7.视图层:
?studentList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://jsp.veryedu.cn" prefix="z"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link
href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css"
rel="stylesheet">
<script
src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.0/js/bootstrap.js"></script>
<title>学生表</title>
<style type="text/css">
.page-item input {
padding: 0;
width: 40px;
height: 100%;
text-align: center;
margin: 0 6px;
}
.page-item input, .page-item b {
line-height: 38px;
float: left;
font-weight: 400;
}
.page-item.go-input {
margin: 0 10px;
}
</style>
</head>
<body>
<form class="form-inline"
action="${pageContext.request.contextPath }/student.action?methodName=list" method="post">
<div class="form-group mb-2">
<input type="text" class="form-control-plaintext" name="sname"
placeholder="请输入学生名字">
<!-- <input name="rows" value="20" type="hidden"> -->
<!-- 不想分页 -->
<input name="pagination" value="false" type="hidden">
</div>
<button type="submit" class="btn btn-primary mb-2">查询</button>
<a class="btn btn-primary mb-2" href="${pageContext.request.contextPath }/student.action?methodName=toEdit">新增</a>
</form>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">学生ID</th>
<th scope="col">学生名</th>
<th scope="col">学生备注</th>
<th scope="col">操作</th>
</tr>
</thead>
<tbody>
<c:forEach var="b" items="${students }">
<tr>
<td>${b.sid }</td>
<td>${b.sname }</td>
<td>${b.remark }</td>
<td>
<a href="${pageContext.request.contextPath }/student.action?methodName=toEdit&sid=${b.sid}">修改</a>
<a href="${pageContext.request.contextPath }/student.action?methodName=delete&sid=${b.sid}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<!-- 这一行代码就相当于前面分页需求前端的几十行了 -->
<z:page pageBean="${pageBean }"></z:page>
</body>
</html>
?8.配置mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<action path="/student" type="com.leijiajia.web.StudentAction">
<forward name="list" path="/studentList.jsp" redirect="false" />
<!-- 增删改操作之后要再一次查询数据库的新的数据做展示 -->
<forward name="toList" path="/student.action?methodName=list" redirect="true" />
<forward name="toEdit" path="/studentEdit.jsp" redirect="false" />
</action>
</config>
|