6.1、Servlet优化
1.最初的做法:一个请求对应一个Servlet
存在的问题:servlet
太多了
2.把一系列的请求对应一个Servlet
IndexServlet
、AddServlet
、EditServlet
、DeleteServlet
、UpdateServlet
合并成FruitServlet
- 通过一个
operation
值来决定调用FruitServlet
中的哪个方法 - 使用的
switch-case
语法来判断方法
【FruitServlet.java】
@WebServlet("/fruit.do")
public class FruitServlet extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String operation = request.getParameter("operation");
if (StringUtil.isEmpty(operation)) {
operation = "index";
}
switch (operation) {
case "index":
index(request,response);
break;
case "add":
add(request,response);
break;
case "delete":
delete(request,response);
break;
case "edit":
edit(request,response);
break;
case "update":
update(request,response);
break;
default:
throw new RuntimeException("operation值非法!!");
}
}
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置编码
// request.setCharacterEncoding("UTF-8");
//2.获取参数
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String countStr = request.getParameter("fcount");
int fcount = Integer.parseInt(countStr);
String remark = request.getParameter("remark");
String FidStr = request.getParameter("fid");
int fid = Integer.parseInt(FidStr);
//3.执行更新
Fruit fruit = new Fruit(fid, fname,price,fcount,remark);
fruitDAO.updateFruit(fruit);
//4.资源跳转
response.sendRedirect("fruit.do");
}
private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String strId = request.getParameter("fid");
if (StringUtil.isNotEmpty(strId)) {
int fid = Integer.parseInt(strId);
Fruit fruit = fruitDAO.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
super.processTemplate("edit", request,response);
}
}
private void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtil.isNotEmpty(fidStr)) {
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
response.sendRedirect("fruit.do");
}
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname = request.getParameter("fname");
int price = Integer.parseInt(request.getParameter("price"));
String remark = request.getParameter("remark");
int fcount = Integer.parseInt(request.getParameter("fcount"));
Fruit fruit = new Fruit(fname,price,fcount,remark);
System.out.println(fruit);
fruitDAO.addFruit(fruit);
response.sendRedirect("fruit.do");
}
private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
//设置当前页:默认值为1
int pageNum = 1;
//
String operate = request.getParameter("operate");
//如果operate!=null,说明:通过表单的查询按钮点击过来的
//如果operate==null,说明:不是通过表单的查询按钮点击过来的
String keyword = null;
if (StringUtil.isNotEmpty(operate) && "search".equals(operate)) {
//说明是点击表单查询发送过来的请求
//此时,pageNum应该还原为1,keyword应该从请求参数中获取
pageNum = 1;
keyword = request.getParameter("keyword");
//如果keyword为null,需要设置为"",否则查询时会拼接成 %null%,我们期望的是 %%
if (StringUtil.isEmpty(keyword)) {
keyword = "";
}
//将keyword把偶才能(覆盖)到session中
session.setAttribute("keyword",keyword);
}else {
//说明此处不是是点击表单查询发送过来的请求,比如点击上一页,下一页或者直接在地址栏输入网址
//此时keyword 应该从session的作用域获取
String pageNumStr = request.getParameter("pageNum");
if(StringUtil.isNotEmpty(pageNumStr)) {
//如果从请求中读取到pageNum,则类型转换,否则默认为1
pageNum = Integer.parseInt(pageNumStr);
}
//如果不是点击的查询按钮,那么查询是基于session中保存的现有keyword进行查询
Object keywordObj = session.getAttribute("keyword");
if (keywordObj != null) {
keyword = (String) keywordObj;
}else {
keyword = "";
}
}
//重新更新当前页的值
session.setAttribute("pageNum",pageNum);
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNum);
//保存到session作用域
session.setAttribute("fruitList", fruitList);
int count = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (count + 5 - 1) / 5;
session.setAttribute("pageCount", pageCount);
super.processTemplate("index",request,response);
}
}
6.2、Servlet优化——使用反射
1.在上一个版本中,Servlet
中充斥着大量的switch-case
,随着项目业务扩大,那么会有很多的Servlet
,也就意味着会有很多的switch-case
。这是一种代码冗余。
2.因此,在Servlet
中使用反射机制,规定operation
的值和反方面一致,那么接收到operation
的值是什么,就表明需要调用对应的方法进行响应,如果找不到对应的方法,则抛异常。
【FruitServlet.java】
@WebServlet("/fruit.do")
public class FruitController extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitDAOImpl();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String operation = request.getParameter("operation");
if (StringUtil.isEmpty(operation)) {
operation = "index";
}
//获取当前类中的所有方法
Method[] methods = this.getClass().getDeclaredMethods();
for (Method m : methods) {
//获取方法名称
String methodName = m.getName();
if (operation.equals(methodName)) {
//找到和operation同名的方法,那么通过反射技术得到它
try {
m.invoke(this,request,response);
return;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
throw new RuntimeException("operation值非法!!");
}
private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置编码
// request.setCharacterEncoding("UTF-8");
//2.获取参数
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String countStr = request.getParameter("fcount");
int fcount = Integer.parseInt(countStr);
String remark = request.getParameter("remark");
String FidStr = request.getParameter("fid");
int fid = Integer.parseInt(FidStr);
//3.执行更新
Fruit fruit = new Fruit(fid, fname,price,fcount,remark);
fruitDAO.updateFruit(fruit);
//4.资源跳转
response.sendRedirect("fruit.do");
}
private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String strId = request.getParameter("fid");
if (StringUtil.isNotEmpty(strId)) {
int fid = Integer.parseInt(strId);
Fruit fruit = fruitDAO.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
super.processTemplate("edit", request,response);
}
}
private void delete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fidStr = request.getParameter("fid");
if (StringUtil.isNotEmpty(fidStr)) {
int fid = Integer.parseInt(fidStr);
fruitDAO.delFruit(fid);
response.sendRedirect("fruit.do");
}
}
private void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname = request.getParameter("fname");
int price = Integer.parseInt(request.getParameter("price"));
String remark = request.getParameter("remark");
int fcount = Integer.parseInt(request.getParameter("fcount"));
Fruit fruit = new Fruit(fname,price,fcount,remark);
System.out.println(fruit);
fruitDAO.addFruit(fruit);
response.sendRedirect("fruit.do");
}
private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
//设置当前页:默认值为1
int pageNum = 1;
//
String operate = request.getParameter("operate");
//如果operate!=null,说明:通过表单的查询按钮点击过来的
//如果operate==null,说明:不是通过表单的查询按钮点击过来的
String keyword = null;
if (StringUtil.isNotEmpty(operate) && "search".equals(operate)) {
//说明是点击表单查询发送过来的请求
//此时,pageNum应该还原为1,keyword应该从请求参数中获取
pageNum = 1;
keyword = request.getParameter("keyword");
//如果keyword为null,需要设置为"",否则查询时会拼接成 %null%,我们期望的是 %%
if (StringUtil.isEmpty(keyword)) {
keyword = "";
}
//将keyword把偶才能(覆盖)到session中
session.setAttribute("keyword",keyword);
}else {
//说明此处不是是点击表单查询发送过来的请求,比如点击上一页,下一页或者直接在地址栏输入网址
//此时keyword 应该从session的作用域获取
String pageNumStr = request.getParameter("pageNum");
if(StringUtil.isNotEmpty(pageNumStr)) {
//如果从请求中读取到pageNum,则类型转换,否则默认为1
pageNum = Integer.parseInt(pageNumStr);
}
//如果不是点击的查询按钮,那么查询是基于session中保存的现有keyword进行查询
Object keywordObj = session.getAttribute("keyword");
if (keywordObj != null) {
keyword = (String) keywordObj;
}else {
keyword = "";
}
}
//重新更新当前页的值
session.setAttribute("pageNum",pageNum);
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNum);
//保存到session作用域
session.setAttribute("fruitList", fruitList);
int count = fruitDAO.getFruitCount(keyword);
int pageCount = (count + 5 - 1) / 5;
session.setAttribute("pageCount", pageCount);
super.processTemplate("index",request,response);
}
}
6.3、DispatcherServlet的引入
6.3.1、XML
1.概念
HTML:超文本标记语言
XML:可扩展的标记语言
HTML是XML的一个子集
2.XML包含三个部分:
1)XML声明:而且声明这一行代码必须在XML文件的第一行
2)DTD 文档类型定义
3)XML正文
【applicationContext.xml】(放在src下)
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!--这个bean标签的作用是:将来servletPath中涉及的名字对应打的是fruit,那么就要FruitController这个类来处理-->
<bean id="fruit" class="com.mvc.fruit.controllers.FruitController"/>
</beans>
6.3.2、Servlet优化:引入DispatcherServlet
1.在上一个版本中使用了反射机制,但是其实还是存在一定的问题:每一个Servlet中都有类似的反射技术的代码。
2.因此,继续抽取设计了中央控制器类:DispatcherServlet类
3.DispatcherServlet
类的工作分为两大部分:
1)根据url
定位到能够处理这个请求的controller
组件:
-
从
url
中提取servletPath
:/fruit.do -> fruit` -
根据
fruit
找到对应的组件:FruitController
,这个对应的依据存储在applicationContext.xml
中。<bean id="fruit" class="com.mvc.fruit.controllers.FruitController"/>
通过
DOM
技术去解析XML文件,在中央控制器中形成一个beanMap
容器,用来存放所有Controller
组件。 -
根据获取到的
operation
的值定位到FruitController
中需要调用的方法。
2)调用Controller
组件中的方法:
-
获取参数
-
获取即将要调用的方法的参数签名信息:
Parameter[] parameters = method.getParameters();
-
通过
parameter.getName()
获取参数的名称; -
Object[] parameterValues
用来存放对应参数的参数值。 -
另外,需要考虑参数的类型问题,需要做类型转换的工作。通过
parameter.getType()
获取参数的类型。
-
-
执行方法
Object methodReturnObj = method.invoke(controllerBeanObj,parameterValues);
-
视图处理
【DispatcherServlet.java】
@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 baenElement = (Element) beanNode;
String beanId = baenElement.getAttribute("id");
String className = baenElement.getAttribute("class");
Class controllerBeanClass = Class.forName(className);
Object beanObj = controllerBeanClass.newInstance();
beanMap.put(beanId, beanObj);
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码
request.setCharacterEncoding("UTF-8");
//假设URL:http://localhost:8080/03mvcDispatcher/hello.do
//那么servletPath是:/hello.do
//我的思路:
// 第1步: /hello.do -> hello
String servletPath = request.getServletPath();
servletPath = servletPath.substring(1);
int lastDotIndex = servletPath.lastIndexOf(".do");
servletPath = servletPath.substring(0, lastDotIndex);
System.out.println("servletPath=" + servletPath);
// 第2步: hello -> HelloController
Object controllerBeanObj = beanMap.get(servletPath);
String operation = request.getParameter("operation");
if (StringUtil.isEmpty(operation)) {
operation = "index";
}
try {
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
for (Method method : methods) {
if (operation.equals(method.getName())){
//1.统一获取请求参数
//1-1.获取当前请求方法的参数,返回参数数组
Parameter[] parameters = method.getParameters();
//1-2.parameterValues用来存放参数的值
Object[] parameterValues = 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)) {
parameterValues[i] = request;
} else if ("response".equals(parameterName)) {
parameterValues[i] = response;
} else if ("session".equals(parameterName)){
parameterValues[i] = request.getSession();
} else {
//从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
Object parameterObj = parameterValue;
String typeName = parameter.getType().getName();
if (parameterObj != null) {
if ("java.lang.Integer".equals(typeName) ) {
parameterObj = Integer.parseInt(parameterValue);
}
}
parameterValues[i] = parameterObj;
}
}
//2.controller组件中的方法调用
method.setAccessible(true);
Object methodReturnObj = method.invoke(controllerBeanObj,parameterValues);
//3.视图处理
String methodReturnStr = (String) methodReturnObj;
if (methodReturnStr.startsWith("redirect:")) {//比如 :redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
} else {
super.processTemplate(methodReturnStr,request,response);
}
}
}
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
【FruitController.java】
public class FruitController extends ViewBaseServlet {
private FruitDAO fruitDAO = new FruitDAOImpl();
private String update(Integer fid,String fname,Integer price,Integer fcount,String remark){
//3.执行更新
Fruit fruit = new Fruit(fid, fname,price,fcount,remark);
fruitDAO.updateFruit(fruit);
//4.资源跳转
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
private String edit(Integer fid,HttpServletRequest request) {
if (fid != null) {
Fruit fruit = fruitDAO.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
//super.processTemplate("edit", request,response);
return "edit";
}
return "error";
}
private String delete(Integer fid) {
if (fid != null) {
fruitDAO.delFruit(fid);
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
return "error";
}
private String add(String fname,Integer price,Integer fcount,String remark) {
Fruit fruit = new Fruit(fname,price,fcount,remark);
System.out.println(fruit);
fruitDAO.addFruit(fruit);
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
private String index(String operate,String keyword,Integer pageNum,HttpServletRequest request) {
HttpSession session = request.getSession();
if (pageNum == null) {
pageNum = 1;
}
if (StringUtil.isNotEmpty(operate) && "search".equals(operate)) {
//说明是点击表单查询发送过来的请求
//此时,pageNum应该还原为1,keyword应该从请求参数中获取
pageNum = 1;
//如果keyword为null,需要设置为"",否则查询时会拼接成 %null%,我们期望的是 %%
if (StringUtil.isEmpty(keyword)) {
keyword = "";
}
//将keyword保存(覆盖)到session中
session.setAttribute("keyword",keyword);
}else {
//说明此处不是是点击表单查询发送过来的请求,比如点击上一页,下一页或者直接在地址栏输入网址
//此时keyword 应该从session的作用域获取
//如果不是点击的查询按钮,那么查询是基于session中保存的现有keyword进行查询
Object keywordObj = session.getAttribute("keyword");
if (keywordObj != null) {
keyword = (String) keywordObj;
}else {
keyword = "";
}
}
//重新更新当前页的值
session.setAttribute("pageNum",pageNum);
List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNum);
//保存到session作用域
session.setAttribute("fruitList", fruitList);
//总记录数
int count = fruitDAO.getFruitCount(keyword);
//总页数
int pageCount = (count + 5 - 1) / 5;
session.setAttribute("pageCount", pageCount);
return "index";
}
}