Java web 2022跟学尚硅谷 四 后端基础
MVC
当前模式
优化后的模式1.8
FruitServlet
package com.atguigu.fruit.servlets;
import com.atguigu.fruit.myspringmvc.ViewBaseServlet;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.fruit.service.FruitService;
import com.atguigu.uils.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @ClassName: FruitServlet
* @Description:
* @Author: wty
* @Date: 2022/12/6
*/
@WebServlet("/fruit.do")
public class FruitServlet extends ViewBaseServlet {
private FruitService fruitService = new FruitService();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String operate = request.getParameter("operate");
if (StringUtils.isEmpty(operate)) {
operate = "index";
}
switch (operate) {
case "index":
index(request, response);
break;
case "add":
add(request, response);
break;
case "del":
del(request, response);
break;
case "edit":
edit(request, response);
break;
case "update":
update(request, response);
break;
default:
throw new RuntimeException("operate值非法!");
}
}
private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Fruit> fruitList = null;
// 分页查询(增加参数pageNumber)
Integer pageNumber = 1;
int sum = 0;
HttpSession session = request.getSession();
// 模糊查询
String oper = request.getParameter("oper");
String keyword = null;
if (StringUtils.isNotEmpty(oper) && "search".equals(oper)) {
// 相当于是模糊查询
keyword = request.getParameter("keyword");
if (StringUtils.isEmpty(keyword)) {
keyword = "";
}
pageNumber = 1;
} else {
// 说明此处不是模糊查询,此时keyword应该从session作用域获取
// 分页查询(增加参数pageNumber)
String pageNumberStr = request.getParameter("pageNumber");
if (StringUtils.isNotEmpty(pageNumberStr)) {
pageNumber = Integer.parseInt(pageNumberStr);
}
Object keywordObj = session.getAttribute("keyword");
if (null != keywordObj) {
keyword = (String) keywordObj;
} else {
keyword = "";
}
}
// 将keyword从session作用域获取
session.setAttribute("keyword", keyword);
fruitList = fruitService.getFruitList(pageNumber, keyword);
// 非分页,显示全部,更新当前页的值
session.setAttribute("pageNumber", pageNumber);
// 获取总条数
sum = fruitService.getFruitCount(keyword);
// 获取总页数pageCount
session.setAttribute("pageCount", ((sum + 5 - 1) / 5));
// 保存到session作用域
//HttpSession session = request.getSession();
session.setAttribute("fruitList", fruitList);
// 此处的视图名称是index
// thymeleaf会将这个逻辑视图名称对应到物理视图名称中去
// 物理视图名称 = view-prefix + 逻辑视图名称 + view-suffix
// 所以真实视图名称: /index.html
super.processTemplate("index", request, response);
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 判断是doGet还是doPost
if (StringUtils.isEmpty(request.getParameter("fname"))
|| StringUtils.isEmpty(request.getParameter("price"))
|| StringUtils.isEmpty(request.getParameter("fcount"))) {
processTemplate("add", request, response);
return;
}
// 获取参数
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
fruitService.addFruit(new Fruit(0, fname, price, fcount, remark));
response.sendRedirect("fruit.do");
}
private void del(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtils.isNotEmpty(fidStr)) {
Integer fid = Integer.parseInt(fidStr);
//fruitService
fruitService.delFruitByFid(fid);
response.sendRedirect("fruit.do");
}
}
private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtils.isNotEmpty(fidStr)) {
Integer fid = Integer.parseInt(fidStr);
Fruit fruit = fruitService.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
processTemplate("edit", request, response);
}
}
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 获取参数
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
// 执行更新
fruitService.updateFruitByFid(new Fruit(fid, fname, price, fcount, remark));
// 资源跳转
//processTemplate("index", request, response);
// 此处需要重定向,目的是重新给indexServlet发请求,重新获取fruitList然后覆盖到session中的数据才是最新的
response.sendRedirect("fruit.do");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
processTemplate("add", request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
index.js
function delFruit(fid) {
if (confirm("是否确认删除?")) {
window.location.href = "fruit.do?fid=" + fid + "&operate=del";
}
}
function page(pageNumber) {
window.location.href = "fruit.do?pageNumber=" + pageNumber;
}
add.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<link rel="stylesheet" href="css/add.css" type="text/css"></link>
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center_p f32">添加库存信息</p>
<form th:action="@{/fruit.do}" method="post">
<input type="hidden" name="operate" value="add"></input>
<table id="table_fruit">
<tr>
<th class="w20">名称:</th>
<td><input type="text" name="fname"/></td>
</tr>
<tr>
<th class="w20">单价:</th>
<td><input type="text" name="price"/></td>
</tr>
<tr>
<th class="w20">库存:</th>
<td><input type="text" name="fcount"/></td>
</tr>
<tr>
<th class="w20">备注:</th>
<td><input type="text" name="remark"/></td>
</tr>
<tr>
<th colspan="2">
<input type="submit" value="添加"/>
</th>
</tr>
</table>
</form>
</div>
</div>
</body>
</html>
index.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/index.css"></link>
<script language="JavaScript" src="js/index.js"></script>
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center_p f32">欢迎使用水果库存后台管理系统</p>
<div id="div_add">
<form id="form_search" th:action="@{/fruit.do}" method="post">
<input type="hidden" name="oper" value="search"></input>
请输入查询关键字:<input type="text" name="keyword" th:value="${session.keyword}"></input>
<input type="submit" value="查询"></input>
</form>
<a th:href="@{/fruit.do(operate = 'add')}" id="a_add">添加库存记录</a>
</div>
<table id="table_fruit">
<tr>
<th class="w20">名称</th>
<th class="w20">单价</th>
<th class="w20">库存</th>
<th class="w20">操作</th>
</tr>
<tr th:if="${#lists.isEmpty(session.fruitList)}">
<td colspan="4">对不起,库存为空!</td>
</tr>
<tr th:unless="${#lists.isEmpty(session.fruitList)}" th:each="fruit : ${session.fruitList}">
<!--<td><a th:text="${fruit.fname}" th:href="@{'/edit.do?fid='+${fruit.fid}}">苹果</a></td>-->
<td><a th:text="${fruit.fname}" th:href="@{/fruit.do(fid=${fruit.fid},operate = 'edit')}">苹果</a></td>
<td th:text="${fruit.price}">5</td>
<td th:text="${fruit.fcount}">20</td>
<!--<td><img src="imgs/del.jpeg" class="delImg" th:οnclick="'delFruit('+${fruit.fid}+')'"></img></td>-->
<td><img src="imgs/del.jpeg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"></img></td>
</tr>
</table>
<div id="div_page">
<div>
<input type="button" value="首 页" th:onclick="page(1)" th:disabled="${session.pageNumber==1}"></input>
<input type="button" value="上一页" th:onclick="|page(${session.pageNumber - 1})|"
th:disabled="${session.pageNumber==1}"></input>
<input type="button" value="下一页" th:onclick="|page(${session.pageNumber + 1})|"
th:disabled="${session.pageNumber==session.pageCount}"></input>
<input type="button" value="尾 页" th:onclick="|page(${session.pageCount})|"
th:disabled="${session.pageNumber==session.pageCount}"></input>
</div>
</div>
</div>
</div>
</body>
</html>
edit.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"></meta>
<link rel="stylesheet" href="css/edit.css" type="text/css"></link>
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center_p f32">编辑库存信息</p>
<form th:action="@{/fruit.do}" method="post" th:object="${fruit}">
<input type="hidden" name="operate" value="update"></input>
<!--隐藏域:功能类似于文本框,它的值会随着表单的发送也会发送给服务器,但是界面上用户看不到-->
<input type="hidden" name="fid" th:value="*{fid}"/>
<table id="table_fruit">
<tr>
<th class="w20">名称:</th>
<!--<td><input type="text" name="fname" th:value="${fruit.fname}"></td>-->
<td><input type="text" name="fname" th:value="*{fname}"/></td>
</tr>
<tr>
<th class="w20">单价</th>
<!--<td><input type="text" name="price" th:value="${fruit.price}"></td>-->
<td><input type="text" name="price" th:value="*{price}"/></td>
</tr>
<tr>
<th class="w20">库存</th>
<!--<td><input type="text" name="fcount" th:value="${fruit.fcount}"></td>-->
<td><input type="text" name="fcount" th:value="*{fcount}"/></td>
</tr>
<tr>
<th class="w20">备注</th>
<!--<td><input type="text" name="remark" th:value="${fruit.remark}"></td>-->
<td><input type="text" name="remark" th:value="*{remark}"/></td>
</tr>
<tr>
<th colspan="2">
<input type="submit" value="修改"/>
</th>
</tr>
</table>
</form>
</div>
</div>
</body>
</html>
继续优化:利用反射1.9
package com.atguigu.fruit.servlets;
import com.atguigu.fruit.myspringmvc.ViewBaseServlet;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.fruit.service.FruitService;
import com.atguigu.uils.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
/**
* @ClassName: FruitServlet
* @Description:
* @Author: wty
* @Date: 2022/12/6
*/
@WebServlet("/fruit.do")
public class FruitServlet extends ViewBaseServlet {
private FruitService fruitService = new FruitService();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String operate = request.getParameter("operate");
if (StringUtils.isEmpty(operate)) {
operate = "index";
}
// 获取当前类中的所有方法
Method[] methods = this.getClass().getDeclaredMethods();
for (Method method : methods) {
String name = method.getName();
if (operate.equals(name)) {
// 找到和operate一样名称的方法,用反射调用
try {
method.invoke(this, request, response);
return;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
throw new RuntimeException("operate值非法!");
}
private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Fruit> fruitList = null;
// 分页查询(增加参数pageNumber)
Integer pageNumber = 1;
int sum = 0;
HttpSession session = request.getSession();
// 模糊查询
String oper = request.getParameter("oper");
String keyword = null;
if (StringUtils.isNotEmpty(oper) && "search".equals(oper)) {
// 相当于是模糊查询
keyword = request.getParameter("keyword");
if (StringUtils.isEmpty(keyword)) {
keyword = "";
}
pageNumber = 1;
} else {
// 说明此处不是模糊查询,此时keyword应该从session作用域获取
// 分页查询(增加参数pageNumber)
String pageNumberStr = request.getParameter("pageNumber");
if (StringUtils.isNotEmpty(pageNumberStr)) {
pageNumber = Integer.parseInt(pageNumberStr);
}
Object keywordObj = session.getAttribute("keyword");
if (null != keywordObj) {
keyword = (String) keywordObj;
} else {
keyword = "";
}
}
// 将keyword从session作用域获取
session.setAttribute("keyword", keyword);
fruitList = fruitService.getFruitList(pageNumber, keyword);
// 非分页,显示全部,更新当前页的值
session.setAttribute("pageNumber", pageNumber);
// 获取总条数
sum = fruitService.getFruitCount(keyword);
// 获取总页数pageCount
session.setAttribute("pageCount", ((sum + 5 - 1) / 5));
// 保存到session作用域
//HttpSession session = request.getSession();
session.setAttribute("fruitList", fruitList);
// 此处的视图名称是index
// thymeleaf会将这个逻辑视图名称对应到物理视图名称中去
// 物理视图名称 = view-prefix + 逻辑视图名称 + view-suffix
// 所以真实视图名称: /index.html
super.processTemplate("index", request, response);
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 判断是doGet还是doPost
if (StringUtils.isEmpty(request.getParameter("fname"))
|| StringUtils.isEmpty(request.getParameter("price"))
|| StringUtils.isEmpty(request.getParameter("fcount"))) {
processTemplate("add", request, response);
return;
}
// 获取参数
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
fruitService.addFruit(new Fruit(0, fname, price, fcount, remark));
response.sendRedirect("fruit.do");
}
private void del(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtils.isNotEmpty(fidStr)) {
Integer fid = Integer.parseInt(fidStr);
//fruitService
fruitService.delFruitByFid(fid);
response.sendRedirect("fruit.do");
}
}
private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtils.isNotEmpty(fidStr)) {
Integer fid = Integer.parseInt(fidStr);
Fruit fruit = fruitService.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
processTemplate("edit", request, response);
}
}
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 获取参数
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
// 执行更新
fruitService.updateFruitByFid(new Fruit(fid, fname, price, fcount, remark));
// 资源跳转
//processTemplate("index", request, response);
// 此处需要重定向,目的是重新给indexServlet发请求,重新获取fruitList然后覆盖到session中的数据才是最新的
response.sendRedirect("fruit.do");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
processTemplate("add", request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
优化2.0
架构图
DispatcherServlet
package com.atguigu.myssm.myspringmvc;
import com.atguigu.uils.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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 javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* @ClassName: DispatcherServlet
* @Description:
* @Author: wty
* @Date: 2022/12/7
*/
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
private Map<String, Object> beanMap = new HashMap<>();
/**
* @param
* @return
* @description 解析配置文件
* @date 2022/12/7 12:10
* @author wty
**/
public DispatcherServlet() {
}
@Override
public void init() throws ServletException {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
// 1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 2.创建DocumentBuilder
try {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 3.创建Document
Document document = documentBuilder.parse(inputStream);
// 4.获取所有的bean结点
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
Element beanElement = (Element) beanNode;
String beanId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
Class<?> beanObj = Class.forName(className);
// 类对象
Object o = beanObj.newInstance();
// 加入servletContext
//Field servletContextField = beanObj.getDeclaredField("servletContext");
//servletContextField.setAccessible(true);
//servletContextField.set(o, this.getServletContext());
Method setServletContext = beanObj.getDeclaredMethod("setServletContext", ServletContext.class);
setServletContext.invoke(o, this.getServletContext());
beanMap.put(beanId, o);
}
}
} catch (ParserConfigurationException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (IOException | InvocationTargetException e) {
e.printStackTrace();
} catch (SAXException | ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 假设URL是:http://localhost:8080/pro15/hello.do
// 那么servletPath 是hello.do
String servletPath = request.getServletPath();
/// hello.do
System.out.println(servletPath);
// 1.字符串截取 hello.do 得到 hello
servletPath = servletPath.substring(1, servletPath.length() - 3);
System.out.println(servletPath);
// 2. hello 和 helloController对应上
Object contorllerBeanObject = beanMap.get(servletPath);
// 下面的逻辑来自于FruitController
String operate = request.getParameter("operate");
if (StringUtils.isEmpty(operate)) {
operate = "index";
}
try {
Method declaredMethod = contorllerBeanObject.getClass().getDeclaredMethod(operate, HttpServletRequest.class, HttpServletResponse.class);
if (declaredMethod != null) {
declaredMethod.setAccessible(true);
declaredMethod.invoke(contorllerBeanObject, request, response);
} else {
throw new RuntimeException("operate值非法!");
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
ViewBaseServlet
package com.atguigu.myssm.myspringmvc;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName: ViewBaseServlet
* @Description:
* @Author: wty
* @Date: 2022/12/5
*/
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
private ServletContext servletContext;
public void init(ServletContext servletContext) throws ServletException {
// 1.获取ServletContext对象
//ServletContext servletContext = this.getServletContext();
//servletContext = this.getServletContext();
this.servletContext = servletContext;
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
/**
* @param
* @return void
* @description 完成资源的转发和视图的渲染
* @param: templateName
* @param: req
* @param: resp
* @date 2022/12/5 16:26
* @author wty
**/
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, this.servletContext);
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
FruitController
package com.atguigu.fruit.controllers;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.fruit.service.FruitService;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.uils.StringUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
/**
* @ClassName: FruitServlet
* @Description:
* @Author: wty
* @Date: 2022/12/6
*/
public class FruitController extends ViewBaseServlet {
/**
* 之前FruitController是一个Servlet组件,那么其中的init方法一定会被调用
* 之前init方法的内部会调用super.init()
*/
private ServletContext servletContext;
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
try {
super.init(servletContext);
} catch (ServletException e) {
e.printStackTrace();
}
}
private FruitService fruitService = new FruitService();
private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Fruit> fruitList = null;
// 分页查询(增加参数pageNumber)
Integer pageNumber = 1;
int sum = 0;
HttpSession session = request.getSession();
// 模糊查询
String oper = request.getParameter("oper");
String keyword = null;
if (StringUtils.isNotEmpty(oper) && "search".equals(oper)) {
// 相当于是模糊查询
keyword = request.getParameter("keyword");
if (StringUtils.isEmpty(keyword)) {
keyword = "";
}
pageNumber = 1;
} else {
// 说明此处不是模糊查询,此时keyword应该从session作用域获取
// 分页查询(增加参数pageNumber)
String pageNumberStr = request.getParameter("pageNumber");
if (StringUtils.isNotEmpty(pageNumberStr)) {
pageNumber = Integer.parseInt(pageNumberStr);
}
Object keywordObj = session.getAttribute("keyword");
if (null != keywordObj) {
keyword = (String) keywordObj;
} else {
keyword = "";
}
}
// 将keyword从session作用域获取
session.setAttribute("keyword", keyword);
fruitList = fruitService.getFruitList(pageNumber, keyword);
// 非分页,显示全部,更新当前页的值
session.setAttribute("pageNumber", pageNumber);
// 获取总条数
sum = fruitService.getFruitCount(keyword);
// 获取总页数pageCount
session.setAttribute("pageCount", ((sum + 5 - 1) / 5));
// 保存到session作用域
//HttpSession session = request.getSession();
session.setAttribute("fruitList", fruitList);
// 此处的视图名称是index
// thymeleaf会将这个逻辑视图名称对应到物理视图名称中去
// 物理视图名称 = view-prefix + 逻辑视图名称 + view-suffix
// 所以真实视图名称: /index.html
super.processTemplate("index", request, response);
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 判断是doGet还是doPost
if (StringUtils.isEmpty(request.getParameter("fname"))
|| StringUtils.isEmpty(request.getParameter("price"))
|| StringUtils.isEmpty(request.getParameter("fcount"))) {
processTemplate("add", request, response);
return;
}
// 获取参数
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
fruitService.addFruit(new Fruit(0, fname, price, fcount, remark));
response.sendRedirect("fruit.do");
}
private void del(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtils.isNotEmpty(fidStr)) {
Integer fid = Integer.parseInt(fidStr);
//fruitService
fruitService.delFruitByFid(fid);
response.sendRedirect("fruit.do");
}
}
private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtils.isNotEmpty(fidStr)) {
Integer fid = Integer.parseInt(fidStr);
Fruit fruit = fruitService.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
processTemplate("edit", request, response);
}
}
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 获取参数
String fidStr = request.getParameter("fid");
Integer fid = Integer.parseInt(fidStr);
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
// 执行更新
fruitService.updateFruitByFid(new Fruit(fid, fname, price, fcount, remark));
// 资源跳转
//processTemplate("index", request, response);
// 此处需要重定向,目的是重新给indexServlet发请求,重新获取fruitList然后覆盖到session中的数据才是最新的
response.sendRedirect("fruit.do");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
processTemplate("add", request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
优化Controller2.1
新增配置
settings-Build,Execution,Deployment-Compiler-java Compiler
增加一句
-parameters
配置完成后的效果
可以发现参数中是可以获取到实际的名字
FruitController
package com.atguigu.fruit.controllers;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.fruit.service.FruitService;
import com.atguigu.uils.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @ClassName: FruitServlet
* @Description:
* @Author: wty
* @Date: 2022/12/6
*/
public class FruitController {
private FruitService fruitService = new FruitService();
private String index(String oper, String keyword, Integer pageNumber, HttpServletRequest request) {
List<Fruit> fruitList = null;
// 分页查询(增加参数pageNumber)
int sum = 0;
HttpSession session = request.getSession();
if (null == pageNumber) {
pageNumber = 1;
}
// 模糊查询
if (StringUtils.isNotEmpty(oper) && "search".equals(oper)) {
// 相当于是模糊查询
if (StringUtils.isEmpty(keyword)) {
keyword = "";
}
pageNumber = 1;
} else {
// 说明此处不是模糊查询,此时keyword应该从session作用域获取
// 分页查询(增加参数pageNumber)
Object keywordObj = session.getAttribute("keyword");
if (null != keywordObj) {
keyword = (String) keywordObj;
} else {
keyword = "";
}
}
// 将keyword从session作用域获取
session.setAttribute("keyword", keyword);
fruitList = fruitService.getFruitList(pageNumber, keyword);
// 非分页,显示全部,更新当前页的值
session.setAttribute("pageNumber", pageNumber);
// 获取总条数
sum = fruitService.getFruitCount(keyword);
// 获取总页数pageCount
session.setAttribute("pageCount", ((sum + 5 - 1) / 5));
// 保存到session作用域
//HttpSession session = request.getSession();
session.setAttribute("fruitList", fruitList);
// 此处的视图名称是index
// thymeleaf会将这个逻辑视图名称对应到物理视图名称中去
// 物理视图名称 = view-prefix + 逻辑视图名称 + view-suffix
// 所以真实视图名称: /index.html
//super.processTemplate("index", request, response);
return "index";
}
private String add(String fname, Integer price, Integer fcount, String remark, HttpServletRequest request) throws IOException {
request.setCharacterEncoding("UTF-8");
// 判断是doGet还是doPost
//if (StringUtils.isEmpty(request.getParameter("fname"))
// || StringUtils.isEmpty(request.getParameter("price"))
// || StringUtils.isEmpty(request.getParameter("fcount"))) {
// //processTemplate("add", request, response);
// return "add";
//}
if (StringUtils.isEmpty(fname)
|| null == price
|| null == fcount) {
//processTemplate("add", request, response);
return "add";
}
fruitService.addFruit(new Fruit(0, fname, price, fcount, remark));
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
private String del(Integer fid) {
if (null != fid) {
//fruitService
fruitService.delFruitByFid(fid);
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
return "error";
}
private String edit(Integer fid, HttpServletRequest request) {
if (null != fid) {
Fruit fruit = fruitService.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
//processTemplate("edit", request, response);
return "edit";
}
return "error";
}
private String update(Integer fid, String fname, Integer price, Integer fcount, String remark) {
// 执行更新
fruitService.updateFruitByFid(new Fruit(fid, fname, price, fcount, remark));
// 资源跳转
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
}
DispatcherServlet
package com.atguigu.myssm.myspringmvc;
import com.atguigu.uils.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.servlet.ServletContext;
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 javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName: DispatcherServlet
* @Description:
* @Author: wty
* @Date: 2022/12/7
*/
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private Map<String, Object> beanMap = new HashMap<>();
/**
* @param
* @return
* @description 解析配置文件
* @date 2022/12/7 12:10
* @author wty
**/
public DispatcherServlet() {
}
@Override
public void init() throws ServletException {
super.init();
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
// 1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 2.创建DocumentBuilder
try {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// 3.创建Document
Document document = documentBuilder.parse(inputStream);
// 4.获取所有的bean结点
NodeList beanNodeList = document.getElementsByTagName("bean");
for (int i = 0; i < beanNodeList.getLength(); i++) {
Node beanNode = beanNodeList.item(i);
if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
Element beanElement = (Element) beanNode;
String beanId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
Class<?> beanObj = Class.forName(className);
// 类对象
Object o = beanObj.newInstance();
// 加入servletContext
//Field servletContextField = beanObj.getDeclaredField("servletContext");
//servletContextField.setAccessible(true);
//servletContextField.set(o, this.getServletContext());
//Method setServletContext = beanObj.getDeclaredMethod("setServletContext", ServletContext.class);
//setServletContext.invoke(o, this.getServletContext());
beanMap.put(beanId, o);
}
}
} catch (ParserConfigurationException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException | ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 假设URL是:http://localhost:8080/pro15/hello.do
// 那么servletPath 是hello.do
String servletPath = request.getServletPath();
/// hello.do
System.out.println(servletPath);
// 1.字符串截取 hello.do 得到 hello
servletPath = servletPath.substring(1, servletPath.length() - 3);
System.out.println(servletPath);
// 2. hello 和 helloController对应上
Object contorllerBeanObject = beanMap.get(servletPath);
// 下面的逻辑来自于FruitController
String operate = request.getParameter("operate");
if (StringUtils.isEmpty(operate)) {
operate = "index";
}
try {
Method declaredMethods[] = contorllerBeanObject.getClass().getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (operate.equals(declaredMethod.getName())) {
// 1.统一获取请求参数
// 获取当前方法的所有参数,返回参数数组
Parameter[] parameters = declaredMethod.getParameters();
// parametersobjectsValue用来存放参数的值
Object[] parametersobjectsValue = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String parameterName = parameter.getName();
// 如果参数名是request、response、session那么单独处理
if ("request".equals(parameterName)) {
parametersobjectsValue[i] = request;
} else if ("response".equals(parameterName)) {
parametersobjectsValue[i] = response;
} else if ("session".equals(parameterName)) {
parametersobjectsValue[i] = request.getSession();
} else {
// 从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
//当前页面http://localhost:8080/pro16/fruit.do?pageNumber=2
// 最后的2在parameterValue中是"2"会报错argument type mismatch
String typeName = parameter.getType().getName();
if ("java.lang.String".equals(typeName)) {
parametersobjectsValue[i] = parameterValue;
} else if (null != parameterValue && "java.lang.Integer".equals(typeName)) {
parametersobjectsValue[i] = Integer.parseInt(parameterValue);
} else {
parametersobjectsValue[i] = parameterValue;
}
}
}
// 2.controller组件中的方法调用
declaredMethod.setAccessible(true);
Object o = declaredMethod.invoke(contorllerBeanObject, parametersobjectsValue);
// 3.视图处理:
String methodReturnStr = (String) o;
if (methodReturnStr.startsWith("redirect")) {
// 比如 redirect:fruit.do
// 截取后只想要 fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
} else {
// 比如 edit
super.processTemplate(methodReturnStr, request, response);
}
} else {
//throw new RuntimeException("operate值非法!");
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
// 常间错误 argument type mismatch
// 当前页面http://localhost:8080/pro16/fruit.do?pageNumber=2
ViewBaseServlet
package com.atguigu.myssm.myspringmvc;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @ClassName: ViewBaseServlet
* @Description:
* @Author: wty
* @Date: 2022/12/5
*/
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
/**
* @param
* @return void
* @description 完成资源的转发和视图的渲染
* @param: templateName
* @param: req
* @param: resp
* @date 2022/12/5 16:26
* @author wty
**/
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
总结
- 最初的做法是: 一个请求对应一个Servlet,这样存在的问题是servlet太多了
- 把一些列的请求都对应一个Servlet, IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet -> 合并成FruitServlet
通过一个operate的值来决定调用FruitServlet中的哪一个方法
使用的是switch-case - 在上一个版本中,Servlet中充斥着大量的switch-case,试想一下,随着我们的项目的业务规模扩大,那么会有很多的Servlet,也就意味着会有很多的switch-case,这是一种代码冗余
因此,我们在servlet中使用了反射技术,我们规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,如果找不到对应的方法,则抛异常 - 在上一个版本中我们使用了反射技术,但是其实还是存在一定的问题:每一个servlet中都有类似的反射技术的代码。因此继续抽取,设计了中央控制器类:DispatcherServlet
DispatcherServlet这个类的工作分为两大部分:
1.根据url定位到能够处理这个请求的controller组件:
1)从url中提取servletPath : /fruit.do -> fruit
2)根据fruit找到对应的组件:FruitController , 这个对应的依据我们存储在applicationContext.xml中
<bean id=“fruit” class="com.atguigu.fruit.controllers.FruitController/>
通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件
3)根据获取到的operate的值定位到我们FruitController中需要调用的方法
2.调用Controller组件中的方法:- 获取参数
获取即将要调用的方法的参数签名信息: Parameter[] parameters = method.getParameters();
通过parameter.getName()获取参数的名称;
准备了Object[] parameterValues 这个数组用来存放对应参数的参数值
另外,我们需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型 - 执行方法
Object returnObj = method.invoke(controllerBean , parameterValues); - 视图处理
String returnStr = (String)returnObj;
if(returnStr.startWith(“redirect:”)){
…
}else if…
- 获取参数