1.mvc-servlet优化1
将所有Servlet合并为一个
1.新建FruitServlet合并Servlet
2.修改指向其他Servlet的代码,指向FruitServlet,同时传operate参数指明采用什么操作(如add,del,update等)
① FruitServlet
package com.example.myssm.servlets;
import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.fruit.pojo.Fruit;
import com.example.myssm.myspringmvc.ViewBaseServlet;
import com.example.myssm.util.StringUtil;
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;
@WebServlet("/fruit.do")
public class FruitServlet extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("utf-8");
String operate = req.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
switch(operate){
case "index":
index(req,resp);
break;
case "add":
add(req,resp);
break;
case "del":
del(req,resp);
break;
case "edit":
edit(req,resp);
break;
case "update":
update(req,resp);
break;
default:
throw new RuntimeException("operate值非法");
}
}
//UpdateServlet的doPost
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//2 获取参数
String fidStr = req.getParameter("fid");
int fid = Integer.parseInt(fidStr);
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
int fcount = Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
//3 执行更新
fruitDAO.updateFruit(new Fruit(fid,fname,price,fcount,remark));
//4 资源跳转
// super.processTemplate("index",req,resp);
//req.getRequestDispatcher("inex.html").forward(req,resp);
//此处需要重定向,目的是重新给IndexServlet发请求,重新获取fruitList,然后覆盖到session
resp.sendRedirect("fruit.do");
}
//EditServlet得到doGet
protected void edit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fidStr = req.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
Fruit fruit = fruitDAO.getFruitByFid(fid);
req.setAttribute("fruit",fruit);
super.processTemplate("edit",req,resp);
}
}
//delServlet的doGet
protected void del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fidStr = req.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
resp.sendRedirect("fruit.do");
}
}
//addServlet的doPost
protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
fruitDAO.addFruit(new Fruit(0,fname,price,fcount,remark));
resp.sendRedirect("fruit.do");
}
//indexServlet的doGet
protected void index(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Integer pageNo = 1;
String oper = req.getParameter("oper");
//如果oper!=null 说明,通过表单的查询按钮的过来的
//如果oper是空的,说明,不是通过表单的查询按钮点过来的
HttpSession session = req.getSession();
String keyword = null;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1,keyword应该从请求参数中获取
pageNo = 1;
keyword = req.getParameter("keyword");
if(StringUtil.isEmpty(keyword)){
keyword = "";
}
session.setAttribute("keyword",keyword);
}else{
//说明此处不是点击表单查询发送的请求
String pageNoStr = req.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String) keywordObj;
}else{
keyword = "";
}
}
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
//保存到session作用域
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount+5-1)/5;
session.setAttribute("pageCount",pageCount);
//此处的视图名称是index
//thymeleaf会将这个 逻辑视图名称 对应的 物理视图 名称上去
//逻辑视图名称:index
//物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是:/ index .html
super.processTemplate("index",req,resp);
}
}
② 修改index.html(index、查询、edit)
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/index.css">
<script type="text/javascript" src="js/index.js"></script>
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">欢迎使用水果库存后台管理系统</p>
<div style="border:0px;width:60%;margin-left:20%;text-align: right;">
<form th:action="@{/fruit.do}" method="post" style="float: left;">
<input type="hidden" name="oper" value="search">
请输入查询关键字:<input type="text" name="keyword" th:value="${session.keyword}">
<input type="submit" value="查询">
</form>
<a th:href="@{/add1.html}" style="border:0px;margin-bottom: 4px;">添加新库存</a>
</div>
<table id="tbl_fruit">
<tr>
<th class="w20">名称</th>
<th class="w20">单价</th>
<th class="w20">库存</th>
<th>操作</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="@{/fruit.do(fid=${fruit.fid},operate='edit')}"></a></td>
<td th:text="${fruit.price}"></td>
<td th:text="${fruit.fcount}"></td>
<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
</tr>
</table>
<div style="border:0px solid red;width:60%;margin-left:20%;padding-top: 4px" class="center">
<input type="button" value="首 页" th:onclick="|page(1)|" th:disabled="${session.pageNo==1}"/>
<input type="button" value="上一页" th:onclick="|page(${session.pageNo-1})|" th:disabled="${session.pageNo==1}"/>
<input type="button" value="下一页" th:onclick="|page(${session.pageNo+1})|" th:disabled="${session.pageNo==session.pageCount}"/>
<input type="button" value="尾 页" th:onclick="|page(${session.pageCount})|" th:disabled="${session.pageNo==session.pageCount}"/>
</div>
</div>
</div>
</body>
</html>
③ 修改add1.html(add)
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/edit.css">
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">编辑库存信息</p>
<form action="fruit.do" method="post" >
<input type="hidden" name="operate" value="add">
<table id="tbl_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>
④修改edit.xml(update)
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/edit.css">
</head>
<body>
<div id="div_container">
<div id="div_fruit_list">
<p class="center f30">编辑库存信息</p>
<form th:action="@{/fruit.do}" method="post" th:object="${fruit}">
<!--隐藏域:功能类似于文本框,它的值会随着表单的发送也会发送给服务器,但是界面上用户看不到-->
<input type="hidden" name="operate" value="update">
<input type="hidden" name="fid" th:value="*{fid}"/>
<table id="tbl_fruit">
<tr>
<th class="w20">名称:</th>
<td><input type="text" name="fname" th:value="*{fname}"/></td>
</tr>
<tr>
<th class="w20">单价:</th>
<td><input type="text" name="price" th:value="*{price}"/></td>
</tr>
<tr>
<th class="w20">库存:</th>
<td><input type="text" name="fcount" th:value="*{fcount}"/></td>
</tr>
<tr>
<th class="w20">备注:</th>
<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>
⑤修改index.js(del、分页)
function delFruit(fid){
if(confirm("是否确认删除?")){
window.location.href='fruit.do?fid='+fid+'&operate=del';
}
}
function page(pageNo){
window.location.href="fruit.do?pageNo="+pageNo;
}
2.mvc-servlet优化2——reflect
采用反射机制优化FruitServlet
当存在很多方法时,写switch-case显得冗余,使用反射能很好的解决这一问题,而不需要关心具体有多少方法
修改FruitServlet的service方法
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("utf-8");
String operate = req.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
//获取当前类中所有的方法
Method[] methods = this.getClass().getDeclaredMethods();
for (Method m : methods) {
//获取方法名称
String methodName = m.getName();
if(operate.equals(methodName)){
try {
//找到和operate同名的方法,那么通过反射技术调用它
m.invoke(this,req,resp);
return ;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
throw new RuntimeException("operate值非法");
}
3.mvc-servlet优化3——dispatcherServlet引入
dispatcherServlet(中央控制器、核心控制器)
通过dispatcherServlet去调用不同的Servlet(如userServlert,FruitServlet等)
①在java包下新建applicationContext.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<!--设个bean标签的作用是 将来servletpath中涉及的名字对应的fruit,那么就要FruitController这个类来处理-->
<bean id="fruit" class="com.example.myssm.servlets.FruitController" />
</beans>
<!--
1.概念
HTML : 超文本标记语言
XML : 可扩展的标记语言
HTML是XML的子集
2.XML包含三个部分:
1)XML声明,而且声明必须在第一行
2)DTD 文档类型定义
3)XML正文
-->
②重命名FruitServlet为FruitController并修改(FruitController变为普通的类)
package com.example.myssm.servlets;
import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.fruit.pojo.Fruit;
import com.example.myssm.myspringmvc.ViewBaseServlet;
import com.example.myssm.util.StringUtil;
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;
public class FruitController extends ViewBaseServlet {
//之前FruitServlet是一个Servlet组件,那么其中的init方法一定会被调用
//之前的init方法内部有:super.init();
private FruitDAO fruitDAO = new FruitDAOImpl();
//UpdateServlet的doPost
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//2 获取参数
String fidStr = req.getParameter("fid");
int fid = Integer.parseInt(fidStr);
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
int fcount = Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
//3 执行更新
fruitDAO.updateFruit(new Fruit(fid,fname,price,fcount,remark));
//4 资源跳转
// super.processTemplate("index",req,resp);
//req.getRequestDispatcher("inex.html").forward(req,resp);
//此处需要重定向,目的是重新给IndexServlet发请求,重新获取fruitList,然后覆盖到session
resp.sendRedirect("fruit.do");
}
//EditServlet得到doGet
protected void edit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fidStr = req.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
Fruit fruit = fruitDAO.getFruitByFid(fid);
req.setAttribute("fruit",fruit);
super.processTemplate("edit",req,resp);
}
}
//delServlet的doGet
protected void del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fidStr = req.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
resp.sendRedirect("fruit.do");
}
}
//addServlet的doPost
protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
fruitDAO.addFruit(new Fruit(0,fname,price,fcount,remark));
resp.sendRedirect("fruit.do");
}
//indexServlet的doGet
protected void index(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Integer pageNo = 1;
String oper = req.getParameter("oper");
//如果oper!=null 说明,通过表单的查询按钮的过来的
//如果oper是空的,说明,不是通过表单的查询按钮点过来的
HttpSession session = req.getSession();
String keyword = null;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1,keyword应该从请求参数中获取
pageNo = 1;
keyword = req.getParameter("keyword");
if(StringUtil.isEmpty(keyword)){
keyword = "";
}
session.setAttribute("keyword",keyword);
}else{
//说明此处不是点击表单查询发送的请求
String pageNoStr = req.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String) keywordObj;
}else{
keyword = "";
}
}
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
//保存到session作用域
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount+5-1)/5;
session.setAttribute("pageCount",pageCount);
//此处的视图名称是index
//thymeleaf会将这个 逻辑视图名称 对应的 物理视图 名称上去
//逻辑视图名称:index
//物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是:/ index .html
super.processTemplate("index",req,resp);
}
}
③新建DispatcherServlet类
获取所有applicationContext.xml中存的类并保存在beanMap中,根据传入的ServletPath来调用不同Controller(原Servlet)的方法
package com.example.myssm.myspringmvc;
import com.example.myssm.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
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 java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet {
private Map<String,Object> beanMap = new HashMap<>();
public DispatcherServlet(){
try {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
//1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//2.创建documentBuilder对象
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");
Object beanObj = Class.forName(className).newInstance();
beanMap.put(beanId,beanObj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("utf-8");
//假设url是 http://localhost:8080/hello.do
//那么servletPath是:/hello.do
//思路是:
//1./hello.do -> hello
//2.hello -> HelloController
String servletPath = req.getServletPath();
// System.out.println("servletPath = " + servletPath); //servletPath = /hello.do
int lastDotIndex = servletPath.lastIndexOf(".do");
servletPath = servletPath.substring(1,lastDotIndex);
// System.out.println(servletPath); //hello
Object controllerBeanObj = beanMap.get(servletPath);
String operate = req.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
try {
Method method = controllerBeanObj.getClass().getDeclaredMethod(operate, HttpServletRequest.class, HttpServletResponse.class);
if(method!=null){
method.setAccessible(true);
method.invoke(controllerBeanObj,req,resp);
}else{
throw new RuntimeException("operate值非法");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
④ 由于FruitController没有调用构造器,因此没有调用父类ViewBaseServlet的init()方法,因此暂时无法正常运行,在下一节解决
4.mvc-servlet优化4——提取视图资源处理通用代码
解决上节资源跳转问题,将资源跳转放在DispatcherServlet中,且让DispatcherServlet继承ViewBaseServlet即可
① 修改FruitController(将资源跳转操作改为返回字符串,用于说明进行什么操作)
package com.example.myssm.servlets;
import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.fruit.pojo.Fruit;
import com.example.myssm.myspringmvc.ViewBaseServlet;
import com.example.myssm.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
public class FruitController{
private FruitDAO fruitDAO = new FruitDAOImpl();
//UpdateServlet的doPost
protected String update(HttpServletRequest req){
//2 获取参数
String fidStr = req.getParameter("fid");
int fid = Integer.parseInt(fidStr);
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
int fcount = Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
//3 执行更新
fruitDAO.updateFruit(new Fruit(fid,fname,price,fcount,remark));
//4 资源跳转
// super.processTemplate("index",req,resp);
//req.getRequestDispatcher("inex.html").forward(req,resp);
//此处需要重定向,目的是重新给IndexServlet发请求,重新获取fruitList,然后覆盖到session
// resp.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
//EditServlet得到doGet
protected String edit(HttpServletRequest req){
String fidStr = req.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
Fruit fruit = fruitDAO.getFruitByFid(fid);
req.setAttribute("fruit",fruit);
// super.processTemplate("edit",req,resp);
return "edit";
}
return "error";
}
//delServlet的doGet
protected String del(HttpServletRequest req) {
String fidStr = req.getParameter("fid");
if(StringUtil.isNotEmpty(fidStr)){
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
// resp.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
return "error";
}
//addServlet的doPost
protected String add(HttpServletRequest req){
String fname = req.getParameter("fname");
String priceStr = req.getParameter("price");
Integer price = Integer.parseInt(priceStr);
String fcountStr = req.getParameter("fcount");
Integer fcount = Integer.parseInt(fcountStr);
String remark = req.getParameter("remark");
fruitDAO.addFruit(new Fruit(0,fname,price,fcount,remark));
// resp.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
//indexServlet的doGet
protected String index(HttpServletRequest req) {
Integer pageNo = 1;
String oper = req.getParameter("oper");
//如果oper!=null 说明,通过表单的查询按钮的过来的
//如果oper是空的,说明,不是通过表单的查询按钮点过来的
HttpSession session = req.getSession();
String keyword = null;
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1,keyword应该从请求参数中获取
pageNo = 1;
keyword = req.getParameter("keyword");
if(StringUtil.isEmpty(keyword)){
keyword = "";
}
session.setAttribute("keyword",keyword);
}else{
//说明此处不是点击表单查询发送的请求
String pageNoStr = req.getParameter("pageNo");
if(StringUtil.isNotEmpty(pageNoStr)){
pageNo = Integer.parseInt(pageNoStr);
}
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String) keywordObj;
}else{
keyword = "";
}
}
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
//保存到session作用域
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount+5-1)/5;
session.setAttribute("pageCount",pageCount);
//此处的视图名称是index
//thymeleaf会将这个 逻辑视图名称 对应的 物理视图 名称上去
//逻辑视图名称:index
//物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是:/ index .html
// super.processTemplate("index",req,resp);
return "index";
}
}
② DispatcherServlet(继承ViewBaseServlet,完成ViewBaseServlet的inie(),完成资源跳转)
package com.example.myssm.myspringmvc;
import com.example.myssm.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private Map<String,Object> beanMap = new HashMap<>();
public DispatcherServlet(){
}
public void init() throws ServletException {
super.init();
try {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
//1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//2.创建documentBuilder对象
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");
Object beanObj = Class.forName(className).newInstance();
beanMap.put(beanId,beanObj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("utf-8");
//假设url是 http://localhost:8080/hello.do
//那么servletPath是:/hello.do
//思路是:
//1./hello.do -> hello
//2.hello -> HelloController
String servletPath = req.getServletPath();
// System.out.println("servletPath = " + servletPath); //servletPath = /hello.do
int lastDotIndex = servletPath.lastIndexOf(".do");
servletPath = servletPath.substring(1,lastDotIndex);
// System.out.println(servletPath); //hello
Object controllerBeanObj = beanMap.get(servletPath);
String operate = req.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
try {
Method method = controllerBeanObj.getClass().getDeclaredMethod(operate, HttpServletRequest.class);
if(method!=null){
//2.Controller组件中的方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj,req);
//3.视图处理
String methodReturnStr = (String) returnObj;
if(methodReturnStr.startsWith("redirect:")){//如:redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
resp.sendRedirect(redirectStr);
}else{
super.processTemplate(methodReturnStr,req,resp); //如:"edit"
}
}else{
throw new RuntimeException("operate值非法");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
5.mvc-servlet优化5——在核心控制器中统一获取参数及视图处理
参数的获取在DispatcherServlet中完成
① 获取参数的实际名称,对idea进行设置(jdk8 新特性)
② 删除out文件夹下的文件然后重新build
③ 修改FruitController的方法为参数传入的方式
package com.example.myssm.servlets;
import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.fruit.pojo.Fruit;
import com.example.myssm.util.StringUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;
public class FruitController{
private FruitDAO fruitDAO = new FruitDAOImpl();
//UpdateServlet的doPost
protected String update(Integer fid,String fname ,Integer price,Integer fcount,String remark){
//3 执行更新
fruitDAO.updateFruit(new Fruit(fid,fname,price,fcount,remark));
//4 资源跳转
// resp.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
//EditServlet得到doGet
protected String edit(Integer fid,HttpServletRequest req){
if(fid != null){
Fruit fruit = fruitDAO.getFruitByFid(fid);
req.setAttribute("fruit",fruit);
// super.processTemplate("edit",req,resp);
return "edit";
}
return "error";
}
//delServlet的doGet
protected String del(Integer fid) {
if(fid != null){
fruitDAO.delFruit(fid);
// resp.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
return "error";
}
//addServlet的doPost
protected String add(Integer fid,String fname ,Integer price,Integer fcount,String remark){
fruitDAO.addFruit(new Fruit(0,fname,price,fcount,remark));
// resp.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
//indexServlet的doGet
protected String index(String oper,String keyword,Integer pageNo,HttpServletRequest req) {
System.out.println(req);
HttpSession session = req.getSession();
if(pageNo == null){
pageNo = 1;
}
if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
//说明是点击表单查询发送过来的请求
//此时,pageNo应该还原为1,keyword应该从请求参数中获取
pageNo = 1;
if(StringUtil.isEmpty(keyword)){
keyword = "";
}
session.setAttribute("keyword",keyword);
}else{
Object keywordObj = session.getAttribute("keyword");
if(keywordObj!=null){
keyword = (String) keywordObj;
}else{
keyword = "";
}
}
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO = new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
//保存到session作用域
session.setAttribute("fruitList",fruitList);
//总记录条数
int fruitCount = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (fruitCount+5-1)/5;
session.setAttribute("pageCount",pageCount);
// super.processTemplate("index",req,resp);
return "index";
}
}
④ 修改DispatcherServlet获取参数
package com.example.myssm.myspringmvc;
import com.example.myssm.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private Map<String,Object> beanMap = new HashMap<>();
public DispatcherServlet(){
}
public void init() throws ServletException {
super.init();
try {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
//1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//2.创建documentBuilder对象
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");
Object beanObj = Class.forName(className).newInstance();
beanMap.put(beanId,beanObj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("utf-8");
//假设url是 http://localhost:8080/hello.do
//那么servletPath是:/hello.do
//思路是:
//1./hello.do -> hello
//2.hello -> HelloController
String servletPath = req.getServletPath();
// System.out.println("servletPath = " + servletPath); //servletPath = /hello.do
int lastDotIndex = servletPath.lastIndexOf(".do");
servletPath = servletPath.substring(1,lastDotIndex);
// System.out.println(servletPath); //hello
Object controllerBeanObj = beanMap.get(servletPath);
String operate = req.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
try {
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
for (Method method : methods) {
if(operate.equals(method.getName())){
//1.统一获取请求参数
//获取当前方法的参数,返回参数数组
Parameter[] parameters = method.getParameters();
//parameterValues 用来承载参数的值
Object[] parameterValues = new Object[parameters.length];
for(int i = 0;i < parameters.length;i++){
Parameter parameter = parameters[i];
String parameterName = parameter.getName();
if("req".equals(parameterName)){
//如果参数名是req,resp,session 那么久不是通过请求中获取参数的方式了
parameterValues[i] = req;
}else if("resp".equals(parameterName)) {
parameterValues[i] = resp;
}else if("session".equals(parameterName)) {
parameterValues[i] = req.getSession();
}else{
//从请求中获取参数值
String parameterValue = req.getParameter(parameterName);
//类型转换 避免错误: IllegalArgumentException: argument type mismatch
String typeName = parameter.getType().getName();
Object parameterObj = parameterValue;
if(parameterObj != null){
if("java.lang.Integer".equals(typeName)){
parameterObj = Integer.parseInt(parameterValue);
}
}
parameterValues[i] = parameterObj;
}
}
//2.Controller组件中的方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj,parameterValues);
//3.视图处理
String methodReturnStr = (String) returnObj;
if(methodReturnStr.startsWith("redirect:")){//如:redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
resp.sendRedirect(redirectStr);
}else{
super.processTemplate(methodReturnStr,req,resp); //如:"edit"
}
}
}
// }else{
// throw new RuntimeException("operate值非法");
// }
} catch (Exception e) {
e.printStackTrace();
}
}
}
⑤ 启动tomcat容器,访问:http://localhost:8080/fruit.do
若出现问题:java.lang.IllegalArgumentException: InputStream cannot be null
将applicationContext.xml移动到resources包下
回顾
- 最初的做法是: 一个请求对应一个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…
- 获取参数