7.Java Web之mvc-servlet优化

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包下

回顾

  1. 最初的做法是: 一个请求对应一个Servlet,这样存在的问题是servlet太多了
  2. 把一些列的请求都对应一个Servlet, IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet -> 合并成FruitServlet
    通过一个operate的值来决定调用FruitServlet中的哪一个方法
    使用的是switch-case
  3. 在上一个版本中,Servlet中充斥着大量的switch-case,试想一下,随着我们的项目的业务规模扩大,那么会有很多的Servlet,也就意味着会有很多的switch-case,这是一种代码冗余
    因此,我们在servlet中使用了反射技术,我们规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,如果找不到对应的方法,则抛异常
  4. 在上一个版本中我们使用了反射技术,但是其实还是存在一定的问题:每一个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组件中的方法:
    1. 获取参数
      获取即将要调用的方法的参数签名信息: Parameter[] parameters = method.getParameters();
      通过parameter.getName()获取参数的名称;
      准备了Object[] parameterValues 这个数组用来存放对应参数的参数值
      另外,我们需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型
    2. 执行方法
      Object returnObj = method.invoke(controllerBean , parameterValues);
    3. 视图处理
      String returnStr = (String)returnObj;
      if(returnStr.startWith(“redirect:”)){

      }else if…
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected end-of-input within/between Object entries; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected end-of-input within/between Object entries at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 7, column: 17] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:391) ~[spring-web-5.3.20.jar:5.3.20] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:343) ~[spring-web-5.3.20.jar:5.3.20] at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:185) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:160) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:133) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-5.3.20.jar:5.3.20] at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179) ~[spring-web-5.3.20.jar:5.3.20] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146) ~[spring-web-5.3.20.jar:5.3.20] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.3.20.jar:5.3.20] at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) [spring-webmvc-5.3.20.jar:5.3.20] 这个错误啥意思?
最新发布
07-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值