javaweb

一、HTML

1.html语言是解释型语言,不是编译型浏览器是容错的

2.html页面中由一对标签组成:<html></html>
	<html> 称之为 开始标签
	</html>称之为 结束标签
	
3.title 表示网页的标题

4.可以在meta标签中设置编码方式

5.<br/>表示换行,br标签是一个单标签。
	单标签:开始标签和结束标签是同一个,斜杠放在单词后面
	
6.p表示段落标签

7.img 标签图片标签
	src属性表示图片文件的路径
	width和height表示图片的大小
	alt表示图片的提示
	
8.路径的问题:
	(1). 相对路径
	(2). 绝对路径
	
9. h1~h6 : 标题标签

10.列表标签:
	- ol 有序列表
		start 表示从*开始,type 显示的类型:A a I i 1(deafult)
    - ul 无序列表
		type disc(default) , circle , square
	
11. u 下划线 
	b 粗体  
	i 斜体
	
12. 上标 sup   下标 sub

13. HTML中的实体: 小于号 &lt; 大于等于号 &ge; 版权 &copy;

14. span 不换行的块标记

15. a 表示超链接
		href 链接的地址
		target:
			_self 在本窗口打开
			_blank 在一个新窗口打开
			_parent 在父窗口打开
			_top  在顶层窗口打开

16.div 层

17.表格	table
	行		tr
	列		td
	表头列	th

	table中有如下属性(虽然已经淘汰,但是最好了解一下)
	- border:表格边框的粗细
	- width:表格的宽度
	- cellspacing:单元格间距
	- cellpadding:单元格填充

	tr中有一个属性: align -> center , left , right 

	rowspan : 行合并
	colspan : 列合并

18.表单	form

19. input type="text" 表示文本框 , 其中 name属性必须要指定,否则这个文本框的数据将来是不会发送给服务器的
	input type="password" 表示密码框
	input type="radio" 表示单选按钮。需要注意的是,name属性值保持一致,这样才会有互斥的效果;可以通过checked属性设置默认选中的项
	input type="checkbox" 表示复选框。name属性值建议保持一致,这样将来我们服务器端获取值的时候获取的是一个数组
	select 表示下拉列表。每一个选项是option,其中value属性是发送给服务器的值 , selected表示默认选中的项
	textarea 表示多行文本框(或者称之为文本域),它的value值就是开始结束标签之间的内容
	input type="submit" 表示提交按钮
	input type="reset" 表示重置按钮
	input type="button" 表示普通按钮
	
20.frameset 表示页面框架 , 这个标签已经淘汰,了解,不需要掌握
frame表示框架中的具体页面引用
iframe
<!--
 * @Descripttion: 
 * @version: 
 * @Author: ZKYAAA
 * @Date: 2022-04-12 16:14:23
 * @LastEditors: 请叫我ZK谕啊啊啊
 * @LastEditTime: 2022-04-12 20:43:18
-->
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="description" content="属性声明了什么">
    <meta name="keywords" content="算法,计算机">
    <title>Acwing-Web应用课</title>
    <link rel="icon" href="/WEB/images/aa.png">
    <!--logo-->>
</head>

<body>
    <div> Hello world </div>
    <div> zrq </div>
    <h1> hello world </h1>
    <span>ZKYAAA</span>

    <h1>1</h1>
    <h2>1</h2>
    <h3>1</h3>
    <h4>1</h4>
    <h5>1</h5>
    <h6>1</h6>
    <p>zjhdkaenkafakjfndakhjfwkaeejf</p>
    <p>ahjierfhawireqwm,jrjehqketiruhqjm34wiuq</p>


    <pre>
        #include"iostream"
        int main(){
            int a;
            int b;
            return 0;
        }
    </pre>
    <img src="/WEB/images/aa.png" alt="mountain" height="300" width="300">

    <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/audio">MDN</a>
    <a href="https://www.acwing.com/activity/content/31/" target="_blank">ACWing</a>


    <from>
        </br>
        <label for="username"></label>
        <input placeholder="用户名" required type="text" name="username" id="username">

        </br>
        <label for="age"></label>
        <input placeholder="年龄" type="number" name="age" id="age">

        </br>
        <label for="email"></label>
        <input placeholder="邮箱" required type="email" name="email" id="emau=il">

        </br>
        <label for="cpp">cpp</label>
        <input type="radio" name="lang" value="cpp">

        </br>
        <label for="java">java</label>
        <input type="radio" name="lang" value="java">
        <br>
        <label for="resume">用户名</label>
        <textarea name="resume" rows="10" cols="10">
        </textarea>

        <br>
        <label for="language"></label>
        <select name="language" id="language">
            <option value=""></option>
            <option selected value="cpp">cpp</option>
            <option value="java">java</option>
            <option value="python">python</option>
        </select>
        <br>
        <button type="button">提交</button>
        </form>

        <br>
        武林高手排行榜:
        <ol type="i" start="3">
            <li>扫地僧</li>
            <li>萧远山</li>
            <li>慕容博</li>
            <li>虚竹</li>
            <li>阿紫</li>
        </ol>
        武林大会人员名单:
        <ul type="circle">
            <li>乔峰</li>
            <li>阿朱</li>
            <li>马夫人</li>
            <li>白世镜</li>
        </ul>

        你是<b><i><u>喜欢</u></i></b><b></b>月饼还是<i></i><u>月饼</u><br />

        水分子的化学式: H<sub>2</sub>O <br />
        氧气的化学式: O<sup>2</sup><br />

        5&lt;10
        10&gt;5
        5&le;10
        10&ge;5
        注册商标 &reg;
        版权符号 &copy;

        <span>赵又廷</span>,夺妻之仇。

        <a href="http://www.baidu.com" target="_self">百度一下</a>

        <table border="1" width="600" cellspacing="0" cellpadding="4">
            <tr align="center">
                <th>姓名</th>
                <th>门派</th>
                <th>成名绝技</th>
                <th>内功值</th>
            </tr>
            <tr align="center">
                <td>乔峰</td>
                <td>丐帮</td>
                <td>少林长拳</td>
                <td>5000</td>
            </tr>
            <tr align="center">
                <td>虚竹</td>
                <td>灵鹫宫</td>
                <td>北冥神功</td>
                <td>15000</td>
            </tr>
            <tr align="center">
                <td>扫地僧</td>
                <td>少林寺</td>
                <td>七十二绝技</td>
                <td>未知</td>
            </tr>
        </table>
        <hr>
        <table border="1" cellspacing="0" cellpadding="4" width="600">
            <tr>
                <th>名称</th>
                <th>单价</th>
                <th>数量</th>
                <th>小计</th>
                <th>操作</th>
            </tr>
            <tr align="center">
                <td>苹果</td>
                <td rowspan="2">5</td>
                <td>20</td>
                <td>100</td>
                <td><img src="/WEB/images/del.png" width="24" height="24" /></td>
            </tr>
            <tr align="center">
                <td>菠萝</td>
                <td>15</td>
                <td>45</td>
                <td><img src="/WEB/images/del.png" width="24" height="24" /></td>
            </tr>
            <tr align="center">
                <td>西瓜</td>
                <td>6</td>
                <td>6</td>
                <td>36</td>
                <td><img src="/WEB/images/del.png" width="24" height="24" /></td>
            </tr>
            <tr align="center">
                <td>总计</td>
                <td colspan="4">181</td>
            </tr>
        </table>

        <form action="***.html" method="post">
            昵称:<input type="text" value="请输入你的昵称" /><br />
            密码:<input type="password" name="pwd" /><br />
            性别:<input type="radio" name="gender" value="male" /><input type="radio" name="gender" value="female" checked /><br />
            爱好:<input type="checkbox" name="hobby" value="basketball" />篮球
            <input type="checkbox" name="hobby" value="football" checked />足球
            <input type="checkbox" name="hobby" value="earth" checked />地球<br />
            星座:<select name="star">
                <option value="1">白羊座</option>
                <option value="2" selected>金牛座</option>
                <option value="3">双子座</option>
                <option value="4">天蝎座</option>
                <option value="5">天秤座</option>
            </select><br />
            备注:<textarea name="remark" rows="4" cols="50"></textarea><br />
            <input type="submit" value=" 注 册 " />
            <input type="reset" value="重置" />
            <input type="button" value="这是一个普通按钮" />
        </form>
</body>

</html>

二、Servlet

1.servelt基础简介

(1)web.xml

1.用户发送请求,action=add
2.项目中,web.xml中找到url-parttern=/add    第十二行
3.找到第11行AddServlet-name=AddServlet
4.找和Servlet-mapping中Servlet-name一致的servlet  找到第7行
5.找到第八行servlet-class  即使com.zky.servlets.AddServlet
6.用户发送的是post请求(method=post) 因此因此 tomcat会执行AddServlet中的doPost方法
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>AddServlet</servlet-name>
        <servlet-class>com.zky.servlets.AddServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AddServlet</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>
</web-app>

(2)servlets下的java类AddServlet

设置编码
tomcat8之前,设置编码:
1.get请求方式:
	get方式目前不需要设置编码(基于tomcat8)
	如果是get请求发送的中文数据,转码稍微有点麻烦(tomcat8之前)
	String fname = request.getParameter("fname");
	(1).将字符串打散成字节数组
        byte[] bytes = fname.getBytes("ISO-8859-1");
	(2).将字节数组按照设定的编码重新组装成字符串
        fname = new String(bytes,"UTF-8");
        
2.post请求方式:
        request.setCharacterEncoding("UTF-8");
    tomcat8开始,设置编码,只需要针对post方式
        request.setCharacterEncoding("UTF-8");
    注意:
        需要注意的是,设置编码(post)这一句代码必须在所有的获取参数动作之前
package com.zky.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-13 23:44
 */
public class AddServlet extends HttpServlet {
    //add.html的from表单的method=post,调用这里的doPost
    //客户端给服务器发请求的时候,服务器端把请求封装为request
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        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");

        System.out.println("fname = "+fname);
        System.out.println("price = "+price);
        System.out.println("fcount = "+fcountStr);
        System.out.println("remark = "+remark);

        FruitDAO fruitDAO = (FruitDAO) new FruitDAOImpl();
        boolean flag = fruitDAO.addFruit(new Fruit(0 , fname , price , fcount , remark));

        System.out.println(flag ? "添加成功!" : "添加失败!");
    }
}

(3)add.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="add" method="post">
    名称:<input type="text" name="fname"/></br>
    价格:<input type="text" name="price"/></br>
    库存:<input type="text" name="fcount"/></br>
    备注: <input type="text" name="remark"/></br>
    <input type="submit" value="添加" />
</form>
</body>
</html>

2.Servlet方法和生命周期

(1)Servlet的继承关系 - 重点查看的是服务方法(service())

1. 继承关系
      javax.servlet.Servlet接口
          javax.servlet.GenericServlet抽象类
              javax.servlet.http.HttpServlet抽象子类
          
2. 相关方法
      javax.servlet.Servlet接口:
        void init(config) - 初始化方法
        void service(request,response) - 服务方法
        void destory() - 销毁方法

      javax.servlet.GenericServlet抽象类:
        void service(request,response) - 仍然是抽象的

      javax.servlet.http.HttpServlet 抽象子类:
        void service(request,response) - 不是抽象的
        (1). String method = req.getMethod(); 获取请求的方式
        (2). 各种if判断,根据请求方式不同,决定去调用不同的do方法
            if (method.equals("GET")) {
                this.doGet(req,resp);
            } else if (method.equals("HEAD")) {
                this.doHead(req, resp);
            } else if (method.equals("POST")) {
                this.doPost(req, resp);
            } else if (method.equals("PUT")) {
        (3). 在HttpServlet这个抽象类中,do方法都差不多:
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String protocol = req.getProtocol();
            String msg = lStrings.getString("http.method_get_not_supported");
            if (protocol.endsWith("1.1")) {
                resp.sendError(405, msg);
            } else {
                resp.sendError(400, msg);
            }
        }
        
3.小结:
    1) 继承关系: HttpServlet -> GenericServlet -> Servlet
    2) Servlet中的核心方法: init() , service() , destroy()
    3) 服务方法: 当有请求过来时,service方法会自动响应(其实是tomcat容器调用的)
            在HttpServlet中我们会去分析请求的方式:到底是get、post、head还是delete等等
            然后再决定调用的是哪个do开头的方法
            那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应的方法,否则默认会报405错误
    4) 因此,我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法
package com.zky.servlets;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-14 21:44
 */
public class Demo02Servlet extends HttpServlet {
    public Demo02Servlet(){
        System.out.println("正在实例化......");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("正在初始化。。。。。");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("正在服务.....");
    }

    @Override
    public void destroy() {
        System.out.println("正在销毁.....");
    }

}

(2)Servlet的生命周期

1.生命周期:从出生到死亡的过程就是生命周期。
	对应Servlet中的三个方法:init(),service(),destroy()
	
2.默认情况下:
 (1)第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
 (2)从第二次请求开始,每一次都是服务
 (3)当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法
 
3.通过案例我们发现:
 (1)Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。
 (2)默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务.这样的好处是什么? 提高系统的启动速度 。 这样的缺点是什么? 第一次请求时,耗时较长。
 (3)因此得出结论: 如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet的初始化时机。
 
4.Servlet的初始化时机:
 (1)默认是第一次接收请求时,实例化,初始化
 (2)我们可以通过<load-on-startup>来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0
 
5.Servlet在容器中是:单例的、线程不安全的
 (1)单例:所有的请求都是同一个实例去响应
 (2)线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
 (3)我们已经知道了servlet是线程不安全的,给我们的启发是: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么不要去:①不要去修改成员变量的值 ②不要去根据成员变量的值做一些逻辑判断
 
 6.Servlet3.0开始支持注解: @WebServlet

3.Http协议

1. Http 称之为 超文本传输协议
2. Http是无状态的
 (1)HTTP 无状态 :服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
 (2)无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱
 (3)通过会话跟踪技术来解决无状态的问题。
        
3. Http请求响应包含两个部分:请求和响应
- 请求:
	请求包含三个部分: 1.请求行 ; 2.请求消息头 ; 3.请求主体
	(1)请求行包含是三个信息: 1. 请求的方式 ; 2.请求的URL ; 3.请求的协议(一般都是HTTP1.1)
	(2)请求消息头中包含了很多客户端需要告诉服务器的信息,比如:我的浏览器型号、版本、我能接收的内容的类型、我给你发的内容的类型、内容的长度等等
	(3)请求体,三种情况
		get方式,没有请求体,但是有一个queryString
		post方式,有请求体,form data
		json格式,有请求体,request payload
		
- 响应:
	响应也包含三本: 1. 响应行 ; 2.响应头 ; 3.响应体
	(1)响应行包含三个信息:1.协议 2.响应状态码(200) 3.响应状态(ok)
	(2)响应头:包含了服务器的信息;服务器发送给浏览器的信息(内容的媒体类型、编码、内容长度等)
	(3)响应体:响应的实际内容(比如请求add.html页面时
		响应的内容就是<html><head><body><form....)

4.会话

1.会话跟踪技术
 (1)客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
 (2)下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,那么服务器就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端
 (3)常用的API:
          request.getSession() -> 获取当前的会话,没有则创建一个新的会话
          request.getSession(true) -> 效果和不带参数相同
          request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的

          session.getId() -> 获取sessionID
          session.isNew() -> 判断当前session是否是新的
          session.getMaxInactiveInterval() -> session的非激活间隔时长,默认1800秒
          session.setMaxInactiveInterval()
          session.invalidate() -> 强制性让会话立即失效
          ....

2.session保存作用域
 (1)session保存作用域是和具体的某一个session对应的
 (2)常用的API:
        void session.setAttribute(k,v)
        Object session.getAttribute(k)
        void removeAttribute(k)
//演示Session
public class Demo03Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Session,如果获取不到,则创建一个新的
        HttpSession session = request.getSession();
        System.out.println("session ID:"+session.getId());
        session.invalidate();
    }
}
//演示向HttpSession保存数据
public class Demo04Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        session.setAttribute("uname","lina");
    }
}
public class Demo05Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        Object uname = session.getAttribute("uname");
        System.out.println(uname);
    }
}

5.服务器内部转发以及客户端重定向

1.服务器内部转发 : request.getRequestDispatcher("...").forward(request,response);
      - 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
      - 地址栏没有变化
2.客户端重定向: response.sendRedirect("....");
      - 两次请求响应的过程。客户端肯定知道请求URL有变化
      - 地址栏有变化
//演示服务器端内部转发以及客户端重定向
public class Demo06Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo06...");
        //服务器端内部转发
        //request.getRequestDispatcher("demo07").forward(request,response);
        response.sendRedirect("demo07");
    }
}
public class Demo07Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo07...");
    }
}

三、Thymeleaf - 视图模板技术

1.步骤

1) 添加thymeleaf的jar包
2) 新建一个Servlet类ViewBaseServlet
3) 在web.xml文件中添加配置
       - 配置前缀 view-prefix
       - 配置后缀 view-suffix
4) 使得我们的Servlet继承ViewBaseServlet

5) 根据逻辑视图名称 得到 物理视图名称
    //此处的视图名称是 index
    //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
    //逻辑视图名称 :   index
    //物理视图名称 :   view-prefix + 逻辑视图名称 + view-suffix
    //所以真实的视图名称是:      /       index       .html
    super.processTemplate("index",request,response)
6) 使用thymeleaf的标签
      th:if   ,  th:unless   , th:each   ,   th:text

2.代码展示

ViewBaseServlet.java

package com.zky.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;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:43
 */
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);

    }

    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());
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 配置上下文参数 -->
    <context-param>
        <param-name>view-prefix</param-name>
        <param-value>/</param-value>
    </context-param>
    <context-param>
        <param-name>view-suffix</param-name>
        <param-value>.html</param-value>
    </context-param>
</web-app>

IndexServlet.java

package com.zky.fruit.servlets;

import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;

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;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:17
 */
//Servlet从3.0开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList();
        System.out.println(fruitList);
        //保存到Session作用域
        HttpSession session = request.getSession();
        session.setAttribute("fruitList",fruitList);
        //此处的视图名称为index
        //那么 thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix+逻辑视图名称+view-suffix
        //所以真实的视图名称:  /index.html
        super.processTemplate("index",request,response);
    }
}

index.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div id="div_container">
	<div id="div_fruit_list">
		<p class="center f30">欢迎使用水果库存后台管理系统</p>
		<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 th:text="${fruit.fname}">苹果</td>
				<td th:text="${fruit.price}">5</td>
				<td th:text="${fruit.fcount}">20</td>
				<td><img src="imgs/del.jpg" class="delImg"/></td>
			</tr>
		</table>
	</div>
</div>
</body>
</html>

3.解析URL地址

<p th:text="@{/aaa/bbb/ccc}">标签体原始值</p>

所以@{}的作用是在字符串前附加『上下文路径』

这个语法的好处是:实际开发过程中,项目在不同环境部署时,Web应用的名字有可能发生变化。所以上下文路径不能写死。而通过@{}动态获取上下文路径后,不管怎么变都不怕啦!

4.保存作用域

(1)保存作用域范围

原始情况下,保存作用域我们可以认为有四个: 
page(页面级别,现在几乎不用) , 
request(一次请求响应范围) , 
session(一次会话范围) , 
application(整个应用程序范围)

1) request:一次请求响应范围
2) session:一次会话范围有效
3) application: 一次应用程序范围有效

(2)request作用域

//演示request保存作用域(demo01和demo02)
@WebServlet("/demo01")
public class Demo01Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.向request保存作用域保存数据
        request.setAttribute("uname","zky");
        //2.客户端重定向
        //response.sendRedirect("demo02");  uname=null

        //3.服务器端转发
        request.getRequestDispatcher("demo02").forward(request,response);

    }
}
@WebServlet("/demo02")
public class Demo02Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取request保存作用域保存的数据,key为uname
        Object uname = request.getAttribute("uname");
        System.out.println("uname="+uname);  //uname=zky
    }
}

(3)session作用域

//演示session保存作用域(demo03和demo04)
@WebServlet("/demo03")
public class Demo03Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.向session保存作用域保存数据
        request.getSession().setAttribute("uname","zky");
        //2.客户端重定向
        response.sendRedirect("demo04");  //uname=null

        //3.服务器端转发
        //request.getRequestDispatcher("demo04").forward(request,response);
    }
}
@WebServlet("/demo04")
public class Demo04Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取session保存作用域保存的数据,key为uname
        Object uname = request.getSession().getAttribute("uname");
        System.out.println("uname="+uname);
    }
}

(3)application作用域

//演示application保存作用域(demo05和demo06)
@WebServlet("/demo05")
public class Demo05Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.向application保存作用域保存数据
        //SercletContext:servlet上下文
        ServletContext application = request.getServletContext();
        application.setAttribute("uname","zky");
        //2.客户端重定向
        response.sendRedirect("demo06");  //uname=null

        //3.服务器端转发
        //request.getRequestDispatcher("demo04").forward(request,response);
    }
}
@WebServlet("/demo06")
public class Demo06Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.获取application保存作用域保存的数据,key为uname
        ServletContext application = request.getServletContext();
        Object uname = application.getAttribute("uname");
        System.out.println("uname="+uname);
    }
}

四、通过servlet实现增删查改

1.编辑模块edit代码开发步骤

(1)先在界面里面利用thymeleaf写好edit.do

这里需要点击苹果可以进行编辑

<td><a th:text="${fruit.fname}" th:href="@{/edit.do(fid=${fruit.fid})}">苹果</a></td>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<link rel="stylesheet" href="css/index.css">
	<script language="JavaScript" src="js/index.js"></script>
</head>
<body>
<div id="div_container">
	<div id="div_fruit_list">
		<p class="center f30">欢迎使用水果库存后台管理系统</p>
		<table id="tbl_fruit">
			<tr>
				<th class="w20">名称1</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="@{'/edit.do?fid='+${fruit.fid}}">苹果</a></td>-->
				<td><a th:text="${fruit.fname}" th:href="@{/edit.do(fid=${fruit.fid})}">苹果</a></td>
				<td th:text="${fruit.price}">5</td>
				<td th:text="${fruit.fcount}">20</td>
<!--				<td><img src="imgs/del.jpg" class="delImg" th:οnclick="'delFruit('+${fruit.fid}+')'"/></td>-->
				<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
			</tr>
		</table>
	</div>
</div>
</body>
</html>

(2)创建servlets文件

使得我们的Servlet继承ViewBaseServlet,使用注解,创建的servlet重写doGet或者doPost方法(除了from表单写的post请求,一般都是get请求),创建实例private FruitDAO fruitDAO=new FruitDAOImpl();

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;
import com.zky.myssm.util.StringUtil;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-16 14:27
 */
@WebServlet("/edit.do")
public class EditServlet extends ViewBaseServlet {

    private FruitDAO fruitDAO=new FruitDAOImpl();

    //除了from表单写的post请求,一般都是get请求
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            
        }
    }
}

(3)在FruitDAO里面创建方法Fruit getFruitByFid(Integer fid),并且重写该类

package com.zky.fruit.dao;

import com.zky.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:12
 */
public interface FruitDAO {
    //获取所有的库序列表信息
    List<Fruit> getFruitList();

    //根据fid获取特定的水果库存信息
    Fruit getFruitByFid(Integer fid);

    //修改指定的库存记录
    void updateFruit(Fruit fruit);

    //根据fid删除指定库存记录
    void delFruit(Integer fid);
}
package com.zky.fruit.dao.impl;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.basedao.BaseDAO;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:15
 */
public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO {

    @Override
    public List<Fruit> getFruitList() {
        return super.executeQuery("select * from t_fruit");
    }

    @Override
    public Fruit getFruitByFid(Integer fid) {
        return super.load("select * from t_fruit where fid = ? " , fid);
    }

    @Override
    public void updateFruit(Fruit fruit) {
        String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? " ;
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid());
    }

    @Override
    public void delFruit(Integer fid) {
        super.executeUpdate("delete from t_fruit where fid = ?",fid);
    }
}

(4)完善servlet方法(执行根据fid获取特定的水果库存信息操作和渲染操作)

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;
import com.zky.myssm.util.StringUtil;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-16 14:27
 */
@WebServlet("/edit.do")
public class EditServlet extends ViewBaseServlet {

    private FruitDAO fruitDAO=new FruitDAOImpl();

    //除了from表单写的post请求,一般都是get请求
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
            super.processTemplate("edit",request,response);
        }
    }
}

2.更新模块update代码开发步骤

(1)先在界面里面利用thymeleaf写好update.do

这里需要点击苹果可以进行编辑

<form th:action="@{/update.do}" method="post" th:object="${fruit}">
<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">编辑库存信息3</p>
		<form th:action="@{/update.do}" method="post" th:object="${fruit}">
			<!-- 隐藏域 : 功能类似于文本框 , 它的值会随着表单的发送也会发送给服务器,但是界面上用户看不到 -->
			<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="${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="*{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>

(2)创建servlets文件

使得我们的Servlet继承ViewBaseServlet,使用注解@WebServlet(“/update.do”),创建的servlet重写doPost方法,创建实例private FruitDAO fruitDAO=new FruitDAOImpl();

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-16 15:25
 */
@WebServlet("/update.do")
public class UpdateServlet extends ViewBaseServlet {
    private FruitDAO fruitDAO=new FruitDAOImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");

        //2.获取参数
        String fidStr = request.getParameter("fid");
        Integer fid = Integer.parseInt(fidStr);
        System.out.println("fid="+fid);

        String fname = request.getParameter("fname");
        System.out.println("fname="+fname);

        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        System.out.println("price="+price);

        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        System.out.println("fcount="+fcount);

        String remark = request.getParameter("remark");
        System.out.println("remark="+remark);
        
    }
}

(3)在FruitDAO里面创建方法void updateFruit(Fruit fruit),并且重写该类

package com.zky.fruit.dao;

import com.zky.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:12
 */
public interface FruitDAO {
    //获取所有的库序列表信息
    List<Fruit> getFruitList();

    //根据fid获取特定的水果库存信息
    Fruit getFruitByFid(Integer fid);

    //修改指定的库存记录
    void updateFruit(Fruit fruit);

    //根据fid删除指定库存记录
    void delFruit(Integer fid);
}
package com.zky.fruit.dao.impl;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.basedao.BaseDAO;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:15
 */
public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO {

    @Override
    public List<Fruit> getFruitList() {
        return super.executeQuery("select * from t_fruit");
    }

    @Override
    public Fruit getFruitByFid(Integer fid) {
        return super.load("select * from t_fruit where fid = ? " , fid);
    }

    @Override
    public void updateFruit(Fruit fruit) {
        String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? " ;
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid());
    }

    @Override
    public void delFruit(Integer fid) {
        super.executeUpdate("delete from t_fruit where fid = ?",fid);
    }
}

(4)完善servlet方法(执行更新操作和资源跳转)

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-16 15:25
 */
@WebServlet("/update.do")
public class UpdateServlet extends ViewBaseServlet {
    private FruitDAO fruitDAO=new FruitDAOImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");

        //2.获取参数
        String fidStr = request.getParameter("fid");
        Integer fid = Integer.parseInt(fidStr);
        System.out.println("fid="+fid);

        String fname = request.getParameter("fname");
        System.out.println("fname="+fname);

        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        System.out.println("price="+price);

        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        System.out.println("fcount="+fcount);

        String remark = request.getParameter("remark");
        System.out.println("remark="+remark);
        //3.执行更新
        fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        response.sendRedirect("index");
    }
}

3.删除模块del代码开发步骤

(1)先在界面里面利用thymeleaf写好delFruit.do

这里需要点击图标可以进行编辑

<td><img src="imgs/del.jpg" class="delImg" th:οnclick="|delFruit(${fruit.fid})|"/</td>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<link rel="stylesheet" href="css/index.css">
	<script language="JavaScript" src="js/index.js"></script>
</head>
<body>
<div id="div_container">
	<div id="div_fruit_list">
		<p class="center f30">欢迎使用水果库存后台管理系统</p>
		<table id="tbl_fruit">
			<tr>
				<th class="w20">名称1</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="@{'/edit.do?fid='+${fruit.fid}}">苹果</a></td>-->
				<td><a th:text="${fruit.fname}" th:href="@{/edit.do(fid=${fruit.fid})}">苹果</a></td>
				<td th:text="${fruit.price}">5</td>
				<td th:text="${fruit.fcount}">20</td>
<!--				<td><img src="imgs/del.jpg" class="delImg" th:οnclick="'delFruit('+${fruit.fid}+')'"/></td>-->
				<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
			</tr>
		</table>
	</div>
</div>
</body>
</html>

删除所需要的js代码

function delFruit(fid){
    if(confirm('是否确认删除?')){
        //location对象就是http://localhost:8088/pro10/index后面加del?fid=
        window.location.href='del.do?fid='+fid;
    }
}

(2)创建servlets文件

使得我们的Servlet继承ViewBaseServlet,使用注解@WebServlet(“/del.do”),创建的servlet重写doGet方法,创建实例private FruitDAO fruitDAO=new FruitDAOImpl();

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.myssm.myspringmvc.ViewBaseServlet;
import com.zky.myssm.util.StringUtil;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-17 15:00
 */
@WebServlet("/del.do")
public class DelServlet extends ViewBaseServlet {
    private FruitDAO fruitDAO=new FruitDAOImpl();
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fidstr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidstr)){
            int fid=Integer.parseInt(fidstr);
            
        }
    }
}

(3)在FruitDAO里面创建方法void delFruit(Integer fid),并且重写该类

package com.zky.fruit.dao;

import com.zky.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:12
 */
public interface FruitDAO {
    //获取所有的库序列表信息
    List<Fruit> getFruitList();

    //根据fid获取特定的水果库存信息
    Fruit getFruitByFid(Integer fid);

    //修改指定的库存记录
    void updateFruit(Fruit fruit);

    //根据fid删除指定库存记录
    void delFruit(Integer fid);
}
package com.zky.fruit.dao.impl;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.basedao.BaseDAO;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:15
 */
public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO {

    @Override
    public List<Fruit> getFruitList() {
        return super.executeQuery("select * from t_fruit");
    }

    @Override
    public Fruit getFruitByFid(Integer fid) {
        return super.load("select * from t_fruit where fid = ? " , fid);
    }

    @Override
    public void updateFruit(Fruit fruit) {
        String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? " ;
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid());
    }

    @Override
    public void delFruit(Integer fid) {
        super.executeUpdate("delete from t_fruit where fid = ?",fid);
    }
}

(4)完善servlet方法(执行删除操作和资源跳转)

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.myssm.myspringmvc.ViewBaseServlet;
import com.zky.myssm.util.StringUtil;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-17 15:00
 */
@WebServlet("/del.do")
public class DelServlet extends ViewBaseServlet {
    private FruitDAO fruitDAO=new FruitDAOImpl();
    @Override
    protected void doGet(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("index");
        }
    }
}

4.添加add模块代码开发步骤

(1)先在界面里面利用thymeleaf写好add.do

这里需要点击link可以进行跳转添加页面

<form th:action="@{/add.do}" method="post">
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<link rel="stylesheet" href="css/add.css">
</head>
<body>
<div id="div_container">
	<div id="div_fruit_list">
		<p class="center f30">编辑库存信息</p>
		<form th:action="@{/add.do}" method="post">
			<table id="tbl_fruit">
				<tr>
					<th class="w20">名称:</th>
					<!-- <td><input type="text" name="fname" th:value="${fruit.fname}"/></td> -->
					<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>

(2)创建servlets文件

使得我们的Servlet继承ViewBaseServlet,使用注解@WebServlet(“/add.do”),创建的servlet重写doPost方法,创建实例private FruitDAO fruitDAO=new FruitDAOImpl();

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;
import com.mysql.cj.ServerPreparedQuery;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-17 16:02
 */
@WebServlet("/add.do")
public class AddServelt extends ViewBaseServlet {
    private FruitDAO fruitDAO=new FruitDAOImpl();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount=Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);

    }
}

(3)在FruitDAO里面创建方法void addFruit(Fruit fruit);,并且重写该类

package com.zky.fruit.dao;

import com.zky.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:12
 */
public interface FruitDAO {
    //获取所有的库序列表信息
    List<Fruit> getFruitList();

    //根据fid获取特定的水果库存信息
    Fruit getFruitByFid(Integer fid);

    //修改指定的库存记录
    void updateFruit(Fruit fruit);

    //根据fid删除指定库存记录
    void delFruit(Integer fid);

    //添加新库存记录
    void addFruit(Fruit fruit);
}

package com.zky.fruit.dao.impl;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.basedao.BaseDAO;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:15
 */
public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO {

    @Override
    public List<Fruit> getFruitList() {
        return super.executeQuery("select * from t_fruit");
    }

    @Override
    public Fruit getFruitByFid(Integer fid) {
        return super.load("select * from t_fruit where fid = ? " , fid);
    }

    @Override
    public void updateFruit(Fruit fruit) {
        String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? " ;
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid());
    }

    @Override
    public void delFruit(Integer fid) {
        super.executeUpdate("delete from t_fruit where fid = ?",fid);
    }

    @Override
    public void addFruit(Fruit fruit) {
        String sql="insert into t_fruit values(0,?,?,?,?)";
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark());
    }
}

(4)完善servlet方法(执行添加操作和资源跳转)

package com.zky.fruit.servlets;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;
import com.mysql.cj.ServerPreparedQuery;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author ZKYAAA
 * @create 2022-04-17 16:02
 */
@WebServlet("/add.do")
public class AddServelt extends ViewBaseServlet {
    private FruitDAO fruitDAO=new FruitDAOImpl();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount=Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);

        fruitDAO.addFruit(fruit);
        response.sendRedirect("index");

    }
}

PS:报错,清除idea的out重新启动,thymeleaf的渲染问题,add.html修改为不要渲染

<form action="add.do" method="post">

5.index分页操作

(1)index.html里面添加分页的按钮

<div style="width: 60%;margin-left: 20%;border: 1px solid red;padding-top: 4px" class="center">
			<input type="button" value="首 页" class="btn" th:onclick="|page(1)|" th:disabled="${session.pageNo==1}"/>
			<input type="button" value="上一页" class="btn" th:onclick="|page(${session.pageNo-1})|" th:disabled="${session.pageNo==1}"/>
			<input type="button" value="下一页" class="btn" th:onclick="|page(${session.pageNo+1})|" th:disabled="${session.pageNo==session.pageCount}"/>
			<input type="button" value="尾 页" class="btn" th:onclick="|page(${session.pageCount})|" th:disabled="${session.pageNo==session.pageCount}"/>
</div>

分页需要的JS代码

 function page(pageNo){
     window.location.href='index?pageNo='+pageNo;
 }

(2)FruitDAO类里面添加分页所需要的方法

获取指定页码上的库存信息,每页显示5条ListgetFruitList(Integer pageNo);

查询库存总记录条数int getFruitCount();

package com.zky.fruit.dao;

import com.zky.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:12
 */
public interface FruitDAO {
    //获取所有的库序列表信息
//    List<Fruit> getFruitList();

    //获取指定页码上的库存信息,每页显示5条
    List<Fruit> getFruitList(Integer pageNo);

    //根据fid获取特定的水果库存信息
    Fruit getFruitByFid(Integer fid);

    //修改指定的库存记录
    void updateFruit(Fruit fruit);

    //根据fid删除指定库存记录
    void delFruit(Integer fid);

    //添加新库存记录
    void addFruit(Fruit fruit);

    //查询库存总记录条数
    int getFruitCount();
}
package com.zky.fruit.dao.impl;

import com.zky.fruit.dao.FruitDAO;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.basedao.BaseDAO;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:15
 */
public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO {
    @Override
    public List<Fruit> getFruitList(Integer pageNo) {
        //SELECT * FROM t_fruit LIMIT 5*(pageNo-1), 5;
        return super.executeQuery("SELECT * FROM t_fruit LIMIT ?, 5",(pageNo-1)*5);
    }

    @Override
    public Fruit getFruitByFid(Integer fid) {
        return super.load("select * from t_fruit where fid = ? " , fid);
    }

    @Override
    public void updateFruit(Fruit fruit) {
        String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? " ;
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid());
    }

    @Override
    public void delFruit(Integer fid) {
        super.executeUpdate("delete from t_fruit where fid = ?",fid);
    }

    @Override
    public void addFruit(Fruit fruit) {
        String sql="insert into t_fruit values(0,?,?,?,?)";
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark());
    }

    @Override
    public int getFruitCount() {
        return ((Long) super.executeComplexQuery("select count(*) from t_fruit")[0]).intValue();
    }
}

(3)创建servlets文件

package com.zky.fruit.servlets;

import com.zky.fruit.dao.impl.FruitDAOImpl;
import com.zky.fruit.pojo.Fruit;
import com.zky.myssm.myspringmvc.ViewBaseServlet;
import com.zky.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;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:17
 */
//Servlet从3.0开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Integer pageNo=1;
        String pageNostr = request.getParameter("pageNo");
        if(StringUtil.isNotEmpty(pageNostr)){
            pageNo = Integer.parseInt(pageNostr);
        }

        HttpSession session = request.getSession();
        session.setAttribute("pageNo",pageNo);

        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(pageNo);
        System.out.println(fruitList);

        //总记录条数
        int fruitCount=fruitDAO.getFruitCount();
        //总页数
        int pageCount=(fruitCount+5-1)/5;
        session.setAttribute("pageCount",pageCount);
        /*
        总记录条数      总页数
        1               1
        5               1
        6               2
        10              2
        11              3
        fruitCount      (fruitCount+5-1)/5
         */
        //保存到Session作用域

        session.setAttribute("fruitList",fruitList);
        //此处的视图名称为index
        //那么 thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix+逻辑视图名称+view-suffix
        //所以真实的视图名称:  /index.html
        super.processTemplate("index",request,response);
    }
}

(4)完善首页和尾页点击细节

th:disabled="${session.pageNo==1}"
th:disabled="${session.pageNo==1}"
th:disabled="${session.pageNo==session.pageCount}"
th:disabled="${session.pageNo==session.pageCount}"

ps:报错

java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.Integer
@Override
public int getFruitCount() {
	return ((Long) super.executeComplexQuery("select count(*) from t_fruit")[0]).intValue();
}

6.添加根据关键字查询的功能

(1)index.html里面添查询的表单

<form th:action="@{/index}" method="post" style="float: left;width: 60%;margin-left: 20%;">
				<input type="hidden" name="oper" value="search">
				请输入查询关键字:<input type="text" name="keyword" th:value="${session.keyword}">
				<input type="submit" value="查询" class="btn">
</form>

(2)FruitDAO类里面完善方法

获取指定页码上的库存信息,每页显示5条List getFruitList(String keyword,Integer pageNo);

查询库存总记录条数int getFruitCount(String keyword);

package com.atguigu.fruit.dao;

import com.atguigu.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:12
 */
public interface FruitDAO {
    //获取所有的库序列表信息
//    List<Fruit> getFruitList();

    //获取指定页码上的库存信息,每页显示5条
    //List<Fruit> getFruitList(Integer pageNo);

    //获取指定页码上的库存信息,每页显示5条
    List<Fruit> getFruitList(String keyword,Integer pageNo);

    //根据fid获取特定的水果库存信息
    Fruit getFruitByFid(Integer fid);

    //修改指定的库存记录
    void updateFruit(Fruit fruit);

    //根据fid删除指定库存记录
    void delFruit(Integer fid);

    //添加新库存记录
    void addFruit(Fruit fruit);

    //查询库存总记录条数
    //int getFruitCount();

    //查询库存总记录条数
    int getFruitCount(String keyword);
}
package com.atguigu.fruit.dao.impl;

import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.basedao.BaseDAO;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:15
 */
public class FruitDAOImpl extends BaseDAO<Fruit> implements FruitDAO {
//    @Override
//    public List<Fruit> getFruitList(Integer pageNo) {
//        //SELECT * FROM t_fruit LIMIT 5*(pageNo-1), 5;
//        return super.executeQuery("SELECT * FROM t_fruit LIMIT ?, 5",(pageNo-1)*5);
//    }

    @Override
    public List<Fruit> getFruitList(String keyword, Integer pageNo) {
        return super.executeQuery("SELECT * FROM t_fruit where fname like ? or remark like ? LIMIT ?, 5","%"+keyword+"%","%"+keyword+"%",(pageNo-1)*5);
    }

    @Override
    public Fruit getFruitByFid(Integer fid) {
        return super.load("select * from t_fruit where fid = ? " , fid);
    }

    @Override
    public void updateFruit(Fruit fruit) {
        String sql = "update t_fruit set fname = ? , price = ? , fcount = ? , remark = ? where fid = ? " ;
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark(),fruit.getFid());
    }

    @Override
    public void delFruit(Integer fid) {
        super.executeUpdate("delete from t_fruit where fid = ?",fid);
    }

    @Override
    public void addFruit(Fruit fruit) {
        String sql="insert into t_fruit values(0,?,?,?,?)";
        super.executeUpdate(sql,fruit.getFname(),fruit.getPrice(),fruit.getFcount(),fruit.getRemark());
    }

    @Override
    public int getFruitCount(String keyword) {
        return ((Long) super.executeComplexQuery("select count(*) from t_fruit where fname like ? or remark like ?","%"+keyword+"%","%"+keyword+"%")[0]).intValue();
    }

//    @Override
//    public int getFruitCount() {
//        return ((Long) super.executeComplexQuery("select count(*) from t_fruit")[0]).intValue();
//    }
}

(3)修改servlets文件

package com.atguigu.fruit.servlets;

import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.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;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:17
 */
//Servlet从3.0开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);

    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        String keyword=null;

        HttpSession session = request.getSession();

        //设置当前页,默认值为1
        Integer pageNo=1;

        String operstr = request.getParameter("oper");
        //如果oper!=null 说明 通过表单的查询按钮点击过来的
        //如果oper==null 说明 不是通过表单的查询按钮点击过来的

        if(StringUtil.isNotEmpty(operstr) && "search".equals(operstr)){
            //说明是点击表单查询发送过来的请求
            //此时,pageNo应该还原为1 ,keyword应该从请求参数中获取
            pageNo=1;
            keyword=request.getParameter("keyword");
            //如果keyword为null,需要设置为空字符串"",否则查询的时候会拼接为%null%,我们期望的是%%
            if(StringUtil.isEmpty(keyword)){
                keyword="";
            }
            //将keyword保存(覆盖)到session中
            session.setAttribute("keyword",keyword);
        }else {
            //说明此处不是通过表单的查询按钮发送过来的请求(此处如点击下面的上一页下一页或者直接在地址栏输入网址)
            //此时keyword应该从session里面获取
            String pageNostr = request.getParameter("pageNo");
            if(StringUtil.isNotEmpty(pageNostr)){
                //如果从请求中读取到pageNo,则类型转换,否则pageNo默认是1
                pageNo = Integer.parseInt(pageNostr);
            }
            //如果点击的查询按钮,那么是基于session中保存的现有的keyword进行的查询
            Object keywordobj = session.getAttribute("keyword");
            if(keywordobj!=null){
                keyword=(String)keywordobj;
            }else {
                keyword="";
            }
        }

        //重新更新当前页的值
        session.setAttribute("pageNo",pageNo);

        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
        System.out.println(fruitList);

        //总记录条数
        int fruitCount=fruitDAO.getFruitCount(keyword);
        //总页数
        int pageCount=(fruitCount+5-1)/5;
        session.setAttribute("pageCount",pageCount);
        /*
        总记录条数      总页数
        1               1
        5               1
        6               2
        10              2
        11              3
        fruitCount      (fruitCount+5-1)/5
         */
        //保存到Session作用域

        session.setAttribute("fruitList",fruitList);
        //此处的视图名称为index
        //那么 thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix+逻辑视图名称+view-suffix
        //所以真实的视图名称:  /index.html
        super.processTemplate("index",request,response);
    }
}

五、MVC思想

1.MVC初始版本

(1)整合FruitServlet

package com.atguigu.fruit.servlets;

import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.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;

/**
 * @author ZKYAAA
 * @create 2022-04-18 14:17
 */
@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 operate = request.getParameter("operate");
        if(StringUtil.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 update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");

        //2.获取参数
        String fidStr = request.getParameter("fid");
        Integer fid = Integer.parseInt(fidStr);
        System.out.println("fid="+fid);

        String fname = request.getParameter("fname");
        System.out.println("fname="+fname);

        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        System.out.println("price="+price);

        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        System.out.println("fcount="+fcount);

        String remark = request.getParameter("remark");
        System.out.println("remark="+remark);
        //3.执行更新
        fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        response.sendRedirect("fruit.do");
    }
    private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
            super.processTemplate("edit",request,response);
        }
    }

    private void del(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 {
        request.setCharacterEncoding("utf-8");

        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount=Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);

        fruitDAO.addFruit(fruit);
        response.sendRedirect("fruit.do");

    }

    private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();

        String keyword=null;

        //设置当前页,默认值为1
        Integer pageNo=1;

        String operstr = request.getParameter("oper");
        //如果oper!=null 说明 通过表单的查询按钮点击过来的
        //如果oper==null 说明 不是通过表单的查询按钮点击过来的

        if(StringUtil.isNotEmpty(operstr) && "search".equals(operstr)){
            //说明是点击表单查询发送过来的请求
            //此时,pageNo应该还原为1 ,keyword应该从请求参数中获取
            pageNo=1;
            keyword=request.getParameter("keyword");
            //如果keyword为null,需要设置为空字符串"",否则查询的时候会拼接为%null%,我们期望的是%%
            if(StringUtil.isEmpty(keyword)){
                keyword="";
            }
            //将keyword保存(覆盖)到session中
            session.setAttribute("keyword",keyword);
        }else {
            //说明此处不是通过表单的查询按钮发送过来的请求(此处如点击下面的上一页下一页或者直接在地址栏输入网址)
            //此时keyword应该从session里面获取
            String pageNostr = request.getParameter("pageNo");
            if(StringUtil.isNotEmpty(pageNostr)){
                //如果从请求中读取到pageNo,则类型转换,否则pageNo默认是1
                pageNo = Integer.parseInt(pageNostr);
            }
            //如果点击的查询按钮,那么是基于session中保存的现有的keyword进行的查询
            Object keywordobj = session.getAttribute("keyword");
            if(keywordobj!=null){
                keyword=(String)keywordobj;
            }else {
                keyword="";
            }
        }

        //重新更新当前页的值
        session.setAttribute("pageNo",pageNo);

        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
        System.out.println(fruitList);

        //总记录条数
        int fruitCount=fruitDAO.getFruitCount(keyword);
        //总页数
        int pageCount=(fruitCount+5-1)/5;
        session.setAttribute("pageCount",pageCount);
        /*
        总记录条数      总页数
        1               1
        5               1
        6               2
        10              2
        11              3
        fruitCount      (fruitCount+5-1)/5
         */
        //保存到Session作用域

        session.setAttribute("fruitList",fruitList);
        //此处的视图名称为index
        //那么 thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix+逻辑视图名称+view-suffix
        //所以真实的视图名称:  /index.html
        super.processTemplate("index",request,response);
    }
}

(2)修改.html文件

add.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<link rel="stylesheet" href="css/add.css">
</head>
<body>
<div id="div_container">
	<div id="div_fruit_list">
		<p class="center f30">编辑库存信息1</p>
<!--		<form th:action="@{/fruit.do}" method="post">-->
		<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" th:value="${fruit.fname}"/></td> -->
					<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.html

<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">编辑库存信息3</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="${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="*{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.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
	<link rel="stylesheet" href="css/index.css">
	<script language="JavaScript" src="js/index.js"></script>
</head>
<body>
<div id="div_container">
	<div id="div_fruit_list">
		<p class="center w20">欢迎使用水果库存后台管理系统</p>
		<div style="border: 0px solid red;width: 60%;margin-left: 20%;text-align: right;">
			<form th:action="@{/fruit.do}" method="post" style="float: left;width: 60%;margin-left: 20%;">
				<input type="hidden" name="oper" value="search">
				请输入查询关键字:<input type="text" name="keyword" th:value="${session.keyword}">
				<input type="submit" value="查询" class="btn">
			</form>
			<a th:href="@{/add.html}" style="border: 0px solid blue;margin-bottom: 0px;">添加新库存记录</a>
		</div>
		<table id="tbl_fruit">
			<tr>
				<th class="w20">名称1</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="@{'/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.jpg" class="delImg" th:οnclick="'delFruit('+${fruit.fid}+')'"/></td>-->
				<td><img src="imgs/del.jpg" class="delImg" th:onclick="|delFruit(${fruit.fid})|"/></td>
			</tr>
		</table>
		<div style="width: 60%;margin-left: 20%;border: 1px solid red;padding-top: 4px" class="center">
			<input type="button" value="首 页" class="btn" th:onclick="|page(1)|" th:disabled="${session.pageNo==1}"/>
			<input type="button" value="上一页" class="btn" th:onclick="|page(${session.pageNo-1})|" th:disabled="${session.pageNo==1}"/>
			<input type="button" value="下一页" class="btn" th:onclick="|page(${session.pageNo+1})|" th:disabled="${session.pageNo==session.pageCount}"/>
			<input type="button" value="尾 页" class="btn" th:onclick="|page(${session.pageCount})|" th:disabled="${session.pageNo==session.pageCount}"/>
		</div>
	</div>
</div>
</body>
</html>

PS:报错1

 org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "@{/fruit.do(fid=${fruit.fid},operate="edit")}" (template: "index" - line 31, col 37)

注意thymeleaf里面的""和’’

<td><a th:text="${fruit.fname}" th:href="@{/fruit.do(fid=${fruit.fid},operate='edit')}">苹果</a></td>

2.MVC-reflect

(1)FruitServlet文件

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置编码
        request.setCharacterEncoding("utf-8");
        String operate = request.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)){
                //找到和operate同名的方法,那么通过反射技术调用它
                try {
                    m.invoke(this,request,response);
                    return;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        throw new RuntimeException("operate值非法!");

    }
package com.atguigu.fruit.servlets;

import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.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;

/**
 * @author ZKYAAA
 * @create 2022-04-18 14:17
 */
@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 operate = request.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)){
                //找到和operate同名的方法,那么通过反射技术调用它
                try {
                    m.invoke(this,request,response);
                    return;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        throw new RuntimeException("operate值非法!");

    }

    private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");

        //2.获取参数
        String fidStr = request.getParameter("fid");
        Integer fid = Integer.parseInt(fidStr);
        System.out.println("fid="+fid);

        String fname = request.getParameter("fname");
        System.out.println("fname="+fname);

        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        System.out.println("price="+price);

        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        System.out.println("fcount="+fcount);

        String remark = request.getParameter("remark");
        System.out.println("remark="+remark);
        //3.执行更新
        fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        response.sendRedirect("fruit.do");
    }
    private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
            super.processTemplate("edit",request,response);
        }
    }

    private void del(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 {
        request.setCharacterEncoding("utf-8");

        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount=Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);

        fruitDAO.addFruit(fruit);
        response.sendRedirect("fruit.do");

    }

    private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();

        String keyword=null;

        //设置当前页,默认值为1
        Integer pageNo=1;

        String operstr = request.getParameter("oper");
        //如果oper!=null 说明 通过表单的查询按钮点击过来的
        //如果oper==null 说明 不是通过表单的查询按钮点击过来的

        if(StringUtil.isNotEmpty(operstr) && "search".equals(operstr)){
            //说明是点击表单查询发送过来的请求
            //此时,pageNo应该还原为1 ,keyword应该从请求参数中获取
            pageNo=1;
            keyword=request.getParameter("keyword");
            //如果keyword为null,需要设置为空字符串"",否则查询的时候会拼接为%null%,我们期望的是%%
            if(StringUtil.isEmpty(keyword)){
                keyword="";
            }
            //将keyword保存(覆盖)到session中
            session.setAttribute("keyword",keyword);
        }else {
            //说明此处不是通过表单的查询按钮发送过来的请求(此处如点击下面的上一页下一页或者直接在地址栏输入网址)
            //此时keyword应该从session里面获取
            String pageNostr = request.getParameter("pageNo");
            if(StringUtil.isNotEmpty(pageNostr)){
                //如果从请求中读取到pageNo,则类型转换,否则pageNo默认是1
                pageNo = Integer.parseInt(pageNostr);
            }
            //如果点击的查询按钮,那么是基于session中保存的现有的keyword进行的查询
            Object keywordobj = session.getAttribute("keyword");
            if(keywordobj!=null){
                keyword=(String)keywordobj;
            }else {
                keyword="";
            }
        }

        //重新更新当前页的值
        session.setAttribute("pageNo",pageNo);

        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
        System.out.println(fruitList);

        //总记录条数
        int fruitCount=fruitDAO.getFruitCount(keyword);
        //总页数
        int pageCount=(fruitCount+5-1)/5;
        session.setAttribute("pageCount",pageCount);
        /*
        总记录条数      总页数
        1               1
        5               1
        6               2
        10              2
        11              3
        fruitCount      (fruitCount+5-1)/5
         */
        //保存到Session作用域

        session.setAttribute("fruitList",fruitList);
        //此处的视图名称为index
        //那么 thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix+逻辑视图名称+view-suffix
        //所以真实的视图名称:  /index.html
        super.processTemplate("index",request,response);
    }
}

3.MVC-dispatcherServlet

(1)src目录下面创建applicationContext.xml

<?xml version="1.0" encoding="utf-8"?>
<beans>
    <!-- 这个bean标签的作用是 将来servletpath中涉及的名字对应的是fruit,那么就要FruitController这个类来处理 -->
    <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
</beans>

<!--
1.概念
HTML : 超文本标记语言
XML : 可扩展的标记语言
HTML是XML的一个子集

2.XML包含三个部分:
1) XML声明 , 而且声明这一行代码必须在XML文件的第一行
2) DTD 文档类型定义
3) XML正文
 -->

(2)将hello—>HelloController两者对应起来

package com.atguigu.myssm.myspringmvc;

import com.atguigu.myssm.util.StringUtil;
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.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 javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ZKYAAA
 * @create 2022-04-18 15:19
 */
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet{
    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;
                    // <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
                    String beanId = beanElement.getAttribute("id");
                    String className=beanElement.getAttribute("class");
                    Object beanObj = Class.forName(className).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:8088/pro15/hello.do
        //那么servletPath是:HelloController
        //hello.do--->hello--->HelloController
        /*思路:
        第一步:/hello.do 字符串处理得到hello

         */
        String servletPath = request.getServletPath(); // 字符串/hello.do
        System.out.println("servletPath="+servletPath);
        servletPath = servletPath.substring(1); //hello.do
        int lastDoIndex=servletPath.lastIndexOf(".do");
        servletPath=servletPath.substring(0,lastDoIndex); //hello
        System.out.println("servletPath1="+servletPath);

        //将hello--->HelloController两者对应起来
        Object controllerBeanObj = beanMap.get(servletPath);
    }
}

(3)修改FruitController的service方法至DispatcherServlet

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;

/**
 * @author ZKYAAA
 * @create 2022-04-15 23:43
 */
public class ViewBaseServlet extends HttpServlet {

    private TemplateEngine templateEngine;

    private ServletContext servletContext;

    public void init(ServletContext servletContext) throws ServletException {
        this.servletContext=servletContext;
        // 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);

    }

    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.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.myssm.util.StringUtil;

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;

/**
 * @author ZKYAAA
 * @create 2022-04-18 14:17
 */

public class FruitController extends ViewBaseServlet {
    //之前FruitService是一个Servlet组件,那么其中得init方法一定会被调用
    //之前得init方法内部会出现一句话:super.init();
    private ServletContext servletContext;

    public void setServletContext(ServletContext servletContext) throws ServletException {
        this.servletContext = servletContext;
        super.init(servletContext);
    }

    private FruitDAO fruitDAO=new FruitDAOImpl();
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


    }

    private void update(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1.设置编码
        request.setCharacterEncoding("utf-8");

        //2.获取参数
        String fidStr = request.getParameter("fid");
        Integer fid = Integer.parseInt(fidStr);
        System.out.println("fid="+fid);

        String fname = request.getParameter("fname");
        System.out.println("fname="+fname);

        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        System.out.println("price="+price);

        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        System.out.println("fcount="+fcount);

        String remark = request.getParameter("remark");
        System.out.println("remark="+remark);
        //3.执行更新
        fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        response.sendRedirect("fruit.do");
    }
    private void edit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
            super.processTemplate("edit",request,response);
        }
    }

    private void del(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 {
        request.setCharacterEncoding("utf-8");

        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount=Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);

        fruitDAO.addFruit(fruit);
        response.sendRedirect("fruit.do");

    }

    private void index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();

        String keyword=null;

        //设置当前页,默认值为1
        Integer pageNo=1;

        String operstr = request.getParameter("oper");
        //如果oper!=null 说明 通过表单的查询按钮点击过来的
        //如果oper==null 说明 不是通过表单的查询按钮点击过来的

        if(StringUtil.isNotEmpty(operstr) && "search".equals(operstr)){
            //说明是点击表单查询发送过来的请求
            //此时,pageNo应该还原为1 ,keyword应该从请求参数中获取
            pageNo=1;
            keyword=request.getParameter("keyword");
            //如果keyword为null,需要设置为空字符串"",否则查询的时候会拼接为%null%,我们期望的是%%
            if(StringUtil.isEmpty(keyword)){
                keyword="";
            }
            //将keyword保存(覆盖)到session中
            session.setAttribute("keyword",keyword);
        }else {
            //说明此处不是通过表单的查询按钮发送过来的请求(此处如点击下面的上一页下一页或者直接在地址栏输入网址)
            //此时keyword应该从session里面获取
            String pageNostr = request.getParameter("pageNo");
            if(StringUtil.isNotEmpty(pageNostr)){
                //如果从请求中读取到pageNo,则类型转换,否则pageNo默认是1
                pageNo = Integer.parseInt(pageNostr);
            }
            //如果点击的查询按钮,那么是基于session中保存的现有的keyword进行的查询
            Object keywordobj = session.getAttribute("keyword");
            if(keywordobj!=null){
                keyword=(String)keywordobj;
            }else {
                keyword="";
            }
        }

        //重新更新当前页的值
        session.setAttribute("pageNo",pageNo);

        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
        System.out.println(fruitList);

        //总记录条数
        int fruitCount=fruitDAO.getFruitCount(keyword);
        //总页数
        int pageCount=(fruitCount+5-1)/5;
        session.setAttribute("pageCount",pageCount);
        /*
        总记录条数      总页数
        1               1
        5               1
        6               2
        10              2
        11              3
        fruitCount      (fruitCount+5-1)/5
         */
        //保存到Session作用域

        session.setAttribute("fruitList",fruitList);
        //此处的视图名称为index
        //那么 thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix+逻辑视图名称+view-suffix
        //所以真实的视图名称:  /index.html
        super.processTemplate("index",request,response);
    }
}

DispatcherServlet实现

package com.atguigu.myssm.myspringmvc;

import com.atguigu.myssm.util.StringUtil;
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.HashMap;
import java.util.Map;

/**
 * @author ZKYAAA
 * @create 2022-04-18 15:19
 */
@WebServlet("*.do")
public class DispatcherServlet extends HttpServlet{
    private Map<String,Object>  beanMap=new HashMap<>();
    //解析配置文件
    public DispatcherServlet(){

    }

    public void 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;
                    // <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
                    String beanId = beanElement.getAttribute("id");
                    String className=beanElement.getAttribute("class");
                    Class<?> controllerBeanClass = Class.forName(className);
                    Object beanObj = Class.forName(className).newInstance();

                    Method setServletContextMethod=controllerBeanClass.getDeclaredMethod("setServletContext", ServletContext.class);
                    setServletContextMethod.invoke(beanObj,this.getServletContext());

                    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();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置编码
        request.setCharacterEncoding("utf-8");

        //假设url是:http://localhost:8088/pro15/hello.do
        //那么servletPath是:HelloController
        //hello.do--->hello--->HelloController
        /*思路:
        第一步:/hello.do 字符串处理得到hello
        第二步:hello->HelloController 或者 fruit->FruitController
         */
        String servletPath = request.getServletPath(); // 字符串/hello.do
        System.out.println("servletPath="+servletPath);
        servletPath = servletPath.substring(1); //hello.do
        int lastDoIndex=servletPath.lastIndexOf(".do");
        servletPath=servletPath.substring(0,lastDoIndex); //hello
        System.out.println("servletPath1="+servletPath);

        //将hello--->HelloController两者对应起来
        Object controllerBeanObj = beanMap.get(servletPath);

        //设置编码
        request.setCharacterEncoding("utf-8");
        String operate = request.getParameter("operate");
        if(StringUtil.isEmpty(operate)){
            operate="index";
        }

        try {
            Method method = controllerBeanObj.getClass().getDeclaredMethod(operate,HttpServletRequest.class,HttpServletResponse.class);

            if(method!=null){
                //java.lang.IllegalAccessException:
                // Class com.atguigu.myssm.myspringmvc.DispatcherServlet can not access a member of class com.atguigu.fruit.controllers.FruitController with modifiers "private"
                method.setAccessible(true);
                method.invoke(controllerBeanObj,request,response);
            }else{
                throw new RuntimeException("operate值非法!");
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

(4)提取视图资源处理通用代码

FruitController.java

package com.atguigu.fruit.controllers;

import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.myssm.util.StringUtil;

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;

/**
 * @author ZKYAAA
 * @create 2022-04-18 14:17
 */

public class FruitController extends ViewBaseServlet {

    private FruitDAO fruitDAO=new FruitDAOImpl();


    private String update(HttpServletRequest request) {

        //2.获取参数
        String fidStr = request.getParameter("fid");
        Integer fid = Integer.parseInt(fidStr);
        System.out.println("fid="+fid);

        String fname = request.getParameter("fname");
        System.out.println("fname="+fname);

        String priceStr = request.getParameter("price");
        int price = Integer.parseInt(priceStr);
        System.out.println("price="+price);

        String fcountStr = request.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        System.out.println("fcount="+fcount);

        String remark = request.getParameter("remark");
        System.out.println("remark="+remark);
        //3.执行更新
        fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        return "redirect:fruit.do";
    }
    private String edit(HttpServletRequest request) throws ServletException{
        String fidStr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidStr)){
            int fid = Integer.parseInt(fidStr);
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
            return "edit";
        }
        return "error";
    }

    private String del(HttpServletRequest request) throws ServletException{
        String fidstr = request.getParameter("fid");
        if(StringUtil.isNotEmpty(fidstr)){
            int fid=Integer.parseInt(fidstr);
            fruitDAO.delFruit(fid);

            //response.sendRedirect("fruit.do");
            return "redirect:fruit.do";
        }
        return "error";
    }

    private String add(HttpServletRequest request) throws ServletException, IOException {
        String fname = request.getParameter("fname");
        Integer price = Integer.parseInt(request.getParameter("price"));
        Integer fcount=Integer.parseInt(request.getParameter("fcount"));
        String remark = request.getParameter("remark");
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);

        fruitDAO.addFruit(fruit);
        //response.sendRedirect("fruit.do");
        return "redirect:fruit.do";
    }

    private String index(HttpServletRequest request) throws ServletException{
        HttpSession session = request.getSession();

        String keyword=null;

        //设置当前页,默认值为1
        Integer pageNo=1;

        String operstr = request.getParameter("oper");
        //如果oper!=null 说明 通过表单的查询按钮点击过来的
        //如果oper==null 说明 不是通过表单的查询按钮点击过来的

        if(StringUtil.isNotEmpty(operstr) && "search".equals(operstr)){
            //说明是点击表单查询发送过来的请求
            //此时,pageNo应该还原为1 ,keyword应该从请求参数中获取
            pageNo=1;
            keyword=request.getParameter("keyword");
            //如果keyword为null,需要设置为空字符串"",否则查询的时候会拼接为%null%,我们期望的是%%
            if(StringUtil.isEmpty(keyword)){
                keyword="";
            }
            //将keyword保存(覆盖)到session中
            session.setAttribute("keyword",keyword);
        }else {
            //说明此处不是通过表单的查询按钮发送过来的请求(此处如点击下面的上一页下一页或者直接在地址栏输入网址)
            //此时keyword应该从session里面获取
            String pageNostr = request.getParameter("pageNo");
            if(StringUtil.isNotEmpty(pageNostr)){
                //如果从请求中读取到pageNo,则类型转换,否则pageNo默认是1
                pageNo = Integer.parseInt(pageNostr);
            }
            //如果点击的查询按钮,那么是基于session中保存的现有的keyword进行的查询
            Object keywordobj = session.getAttribute("keyword");
            if(keywordobj!=null){
                keyword=(String)keywordobj;
            }else {
                keyword="";
            }
        }

        //重新更新当前页的值
        session.setAttribute("pageNo",pageNo);

        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
        System.out.println(fruitList);

        //总记录条数
        int fruitCount=fruitDAO.getFruitCount(keyword);
        //总页数
        int pageCount=(fruitCount+5-1)/5;
        session.setAttribute("pageCount",pageCount);
        /*
        总记录条数      总页数
        1               1
        5               1
        6               2
        10              2
        11              3
        fruitCount      (fruitCount+5-1)/5
         */
        //保存到Session作用域

        session.setAttribute("fruitList",fruitList);
        //此处的视图名称为index
        //那么 thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix+逻辑视图名称+view-suffix
        //所以真实的视图名称:  /index.html
        //super.processTemplate("index",request,response);
        return "index";
    }
}

DispatcherServlet.java

package com.atguigu.myssm.myspringmvc;

import com.atguigu.myssm.util.StringUtil;
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.HashMap;
import java.util.Map;

/**
 * @author ZKYAAA
 * @create 2022-04-18 15:19
 */
@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;
                    // <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
                    String beanId = beanElement.getAttribute("id");
                    String className=beanElement.getAttribute("class");
                    Class<?> controllerBeanClass = Class.forName(className);
                    Object beanObj = Class.forName(className).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:8088/pro15/hello.do
        //那么servletPath是:HelloController
        //hello.do--->hello--->HelloController
        /*思路:
        第一步:/hello.do 字符串处理得到hello
        第二步:hello->HelloController 或者 fruit->FruitController
         */
        String servletPath = request.getServletPath(); // 字符串/hello.do
        System.out.println("servletPath="+servletPath);
        servletPath = servletPath.substring(1); //hello.do
        int lastDoIndex=servletPath.lastIndexOf(".do");
        servletPath=servletPath.substring(0,lastDoIndex); //hello
        System.out.println("servletPath1="+servletPath);

        //将hello--->HelloController两者对应起来
        Object controllerBeanObj = beanMap.get(servletPath);

        //设置编码
        request.setCharacterEncoding("utf-8");
        String operate = request.getParameter("operate");
        if(StringUtil.isEmpty(operate)){
            operate="index";
        }

        try {
            Method method = controllerBeanObj.getClass().getDeclaredMethod(operate,HttpServletRequest.class);

            if(method!=null){
                //java.lang.IllegalAccessException:
                // Class com.atguigu.myssm.myspringmvc.DispatcherServlet can not access a member of class com.atguigu.fruit.controllers.FruitController with modifiers "private"

                //2.controller组件中方法调用
                method.setAccessible(true);
                Object returnObj=method.invoke(controllerBeanObj,request);

                //3.视图处理
                String methodReturnStr=(String) returnObj;
                if(methodReturnStr.startsWith("redirect:")){
                    //比如:redirect:fruit.do
                    String redirectStr = methodReturnStr.substring("redirect:".length());
                    response.sendRedirect(redirectStr);
                }else {
                    super.processTemplate(methodReturnStr,request,response);//比如:"edit"
                }

            }else{
                throw new RuntimeException("operate值非法!");
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

(4)在核心控制器中统一获取参数以及视图处理

review:
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.....

FruitController.java

package com.atguigu.fruit.controllers;

import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.myssm.util.StringUtil;

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;

/**
 * @author ZKYAAA
 * @create 2022-04-18 14:17
 */

public class FruitController extends ViewBaseServlet {

    private FruitDAO fruitDAO=new FruitDAOImpl();


    private String update(Integer fid,String fname,Integer price,Integer fcount,String remark) {
        //3.执行更新
        fruitDAO.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        return "redirect:fruit.do";
    }
    private String edit(Integer fid,HttpServletRequest request){
        if(fid!=null){
            Fruit fruit = fruitDAO.getFruitByFid(fid);
            request.setAttribute("fruit",fruit);
            return "edit";
        }
        return "error";
    }

    private String del(Integer fid,HttpServletRequest request){
        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,HttpServletRequest request) {
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);
        fruitDAO.addFruit(fruit);
        //response.sendRedirect("fruit.do");
        return "redirect:fruit.do";
    }

    private String index(String oper,String keyword,Integer pageNo,HttpServletRequest request) {
        HttpSession session = request.getSession();

        if(pageNo==null){
            pageNo=1;
        }
        if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
            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);

        FruitDAOImpl fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList(keyword,pageNo);
        System.out.println(fruitList);

        //总记录条数
        int fruitCount=fruitDAO.getFruitCount(keyword);
        //总页数
        int pageCount=(fruitCount+5-1)/5;
        session.setAttribute("pageCount",pageCount);

        session.setAttribute("fruitList",fruitList);

        return "index";
    }
}

DispatcherServlet.java

package com.atguigu.myssm.myspringmvc;

import com.atguigu.myssm.util.StringUtil;
import com.mysql.cj.ServerPreparedQuery;
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.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ZKYAAA
 * @create 2022-04-18 15:19
 */
@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;
                    // <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
                    String beanId = beanElement.getAttribute("id");
                    String className=beanElement.getAttribute("class");
                    Class<?> controllerBeanClass = Class.forName(className);
                    Object beanObj = Class.forName(className).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:8088/pro15/hello.do
        //那么servletPath是:HelloController
        //hello.do--->hello--->HelloController
        /*思路:
        第一步:/hello.do 字符串处理得到hello
        第二步:hello->HelloController 或者 fruit->FruitController
         */
        String servletPath = request.getServletPath(); // 字符串/hello.do
        System.out.println("servletPath="+servletPath);
        servletPath = servletPath.substring(1); //hello.do
        int lastDoIndex=servletPath.lastIndexOf(".do");
        servletPath=servletPath.substring(0,lastDoIndex); //hello
        System.out.println("servletPath1="+servletPath);

        //将hello--->HelloController两者对应起来
        Object controllerBeanObj = beanMap.get(servletPath);

        //设置编码
        request.setCharacterEncoding("utf-8");
        String operate = request.getParameter("operate");
        if(StringUtil.isEmpty(operate)){
            operate="index";
        }

        try {
            Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
            for(Method method:methods){
                if(operate.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);
                            String typeName = parameter.getType().getName();

                            Object parameterObj=parameterValue;
                            if(parameterObj!=null){
                                if("java.lang.Integer".equals(typeName)){
                                    parameterObj=Integer.parseInt(parameterValue);
                                }
                            }

                            parameterValues[i] = parameterObj;
                        }
                    }

                    //java.lang.IllegalAccessException:
                    // Class com.atguigu.myssm.myspringmvc.DispatcherServlet can not access a member of class com.atguigu.fruit.controllers.FruitController with modifiers "private"

                    //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());
                        response.sendRedirect(redirectStr);
                    }else {
                        super.processTemplate(methodReturnStr,request,response);//比如:"edit"
                    }

                }
            }

        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}


/*
http://localhost:8088/pro16/fruit.do?pageNo=2
常见错误:java.lang.IllegalArgumentException: argument type mismatch
参数类型不匹配
 */

5.Servlet-API

(1)Servlet的初始化方法

1) Servlet生命周期:实例化、初始化、服务、销毁
2) Servlet中的初始化方法有两个:init() , init(config)
   其中带参数的方法代码如下:
   public void init(ServletConfig config) throws ServletException {
     this.config = config ;
     init();
   }
   
   另外一个无参的init方法如下:
   public void init() throws ServletException{
   }
   
   如果我们想要在Servlet初始化时做一些准备工作,那么我们可以重写init方法
   我们可以通过如下步骤去获取初始化设置的数据
   - 获取config对象:ServletConfig config = getServletConfig();
   - 获取初始化参数值: config.getInitParameter(key);

(2)在web.xml文件中配置Servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>Demo01Servlt</servlet-name>
        <servlet-class>com.atguigu.servlet.Demo01Servlt</servlet-class>
        <init-param>
            <param-name>hello</param-name>
            <param-value>world</param-value>
        </init-param>
        <init-param>
            <param-name>uname</param-name>
            <param-value>zky</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo01Servlt</servlet-name>
        <url-pattern>/demo01</url-pattern>
    </servlet-mapping>
</web-app>

(3通过注解的方式进行配置

package com.atguigu.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;

/**
 * @author ZKYAAA
 * @create 2022-04-19 20:52
 */
@WebServlet(urlPatterns = {"/demo01"},
        initParams = {
                @WebInitParam(name = "hello", value = "world"),
                @WebInitParam(name = "uname", value = "zky")
        }
)
public class Demo01Servlt extends HttpServlet {
    @Override
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        String initValue = config.getInitParameter("hello");
        System.out.println("initValue="+initValue);
    }
}

(4)Servlet中的ServletContext和

通过ServletContext获取配置的上下文参数
1) 获取ServletContext,有很多方法
       在初始化方法中: ServletContxt servletContext = getServletContext();
       在服务方法中也可以通过request对象获取,也可以通过session获取:
       request.getServletContext(); session.getServletContext()
2) 获取初始化值:
       servletContext.getInitParameter();
<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
</context-param>
public class Demo01Servlt extends HttpServlet {
    @Override
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        String initValue = config.getInitParameter("hello");
        System.out.println("initValue="+initValue);

        ServletContext servletContext = getServletContext();
        String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
        System.out.println("contextConfigLocation="+contextConfigLocation);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getServletContext();
        req.getSession().getServletContext();
    }
}

6.MVC

(1)mvc解释

1. Model1和Model2
    MVC : Model(模型)、View(视图)、Controller(控制器)
    视图层:用于做数据展示以及和用户交互的一个界面
    控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件来完成
    模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件
	1) pojo/vo : 值对象
	2) DAO : 数据访问对象
	3) BO : 业务对象
	模型有很多种类:数据访问模型(DAO);业务逻辑模型(BO);值对象模型(POJO);数据传输对象(DTO)

2. 区分业务对象和数据访问对象:
	1) DAO中的方法都是单精度方法或者称之为细粒度方法。什么叫单精度?一个方法只考虑一个操作,比如添加,那就是insert操作、查询那就是select操作....
	2) BO中的方法属于业务方法,也实际的业务是比较复杂的,因此业务方法的粒度是比较粗的
     注册这个功能属于业务功能,也就是说注册这个方法属于业务方法。
     那么这个业务方法中包含了多个DAO方法。也就是说注册这个业务功能需要通过多个DAO方法的组合调用,从而完成注册功能的开发。
     注册:
	(1)检查用户名是否已经被注册 - DAO中的select操作
	(2)向用户表新增一条新用户记录 - DAO中的insert操作
	(3)向用户积分表新增一条记录(新用户默认初始化积分100分) - DAO中的insert操作
	(4)向系统消息表新增一条记录(某某某新用户注册了,需要根据通讯录信息向他的联系人推送消息) - DAO中的insert操作
	(5)向系统日志表新增一条记录(某用户在某IP在某年某月某日某时某分某秒某毫秒注册) - DAO中的insert操作
	(6). .
	
3. 在库存系统中添加业务层组件

(2)代码修改完善,创建service层

接口FruitService

package com.atguigu.fruit.biz;

import com.atguigu.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-19 22:43
 */
public interface FruitService {
    //获取指定页面的库存列表信息
    List<Fruit> getFruitList(String keyword, Integer pageNo);
    //添加库存记录信息
    void addFruit(Fruit fruit);
    //根据Id查看指定库存记录
    Fruit getFruitByid(Integer id);
    //删除特定库存记录
    void delFruit(Integer fid);
    //获取总页数
    Integer getPageCount(String keyword);
    //修改特定库存记录
    void updateFruit(Fruit fruit);

}

接口实现FruitServiceImpl.java

package com.atguigu.fruit.biz.impl;

import com.atguigu.fruit.biz.FruitService;
import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;

import java.util.List;

/**
 * @author ZKYAAA
 * @create 2022-04-19 22:48
 */
public class FruitServiceImpl implements FruitService {

    private FruitDAO fruitDAO=new FruitDAOImpl();
    @Override
    public List<Fruit> getFruitList(String keyword, Integer pageNo) {
        return fruitDAO.getFruitList(keyword,pageNo);
    }

    @Override
    public void addFruit(Fruit fruit) {
        fruitDAO.addFruit(fruit);
    }

    @Override
    public Fruit getFruitByid(Integer id) {
        return fruitDAO.getFruitByFid(id);
    }

    @Override
    public void delFruit(Integer fid) {
        fruitDAO.delFruit(fid);
    }

    @Override
    public Integer getPageCount(String keyword) {
        int count=fruitDAO.getFruitCount(keyword);
        int pageCount=(count+5-1)/5;
        return pageCount;
    }

    @Override
    public void updateFruit(Fruit fruit) {
        fruitDAO.updateFruit(fruit);
    }
}

FruitController.java

package com.atguigu.fruit.controllers;

import com.atguigu.fruit.biz.FruitService;
import com.atguigu.fruit.biz.impl.FruitServiceImpl;
import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.dao.impl.FruitDAOImpl;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.myssm.myspringmvc.ViewBaseServlet;
import com.atguigu.myssm.util.StringUtil;

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;

/**
 * @author ZKYAAA
 * @create 2022-04-18 14:17
 */

public class FruitController extends ViewBaseServlet {

    private FruitService fruitService=new FruitServiceImpl();


    private String update(Integer fid,String fname,Integer price,Integer fcount,String remark) {
        //3.执行更新
        fruitService.updateFruit(new Fruit(fid,fname, price ,fcount ,remark ));

        //4.资源跳转
        //super.processTemplate("index",request,response);
        //request.getRequestDispatcher("index.html").forward(request,response);
        //此处需要重定向,目的是重新给IndexServlet发请求,重新获取furitList,然后覆盖到session中,这样index.html页面上显示的session中的数据才是最新的
        return "redirect:fruit.do";
    }
    private String edit(Integer fid,HttpServletRequest request){
        if(fid!=null){
            Fruit fruit = fruitService.getFruitByid(fid);
            request.setAttribute("fruit",fruit);
            return "edit";
        }
        return "error";
    }

    private String del(Integer fid,HttpServletRequest request){
        if(fid!=null){
            fruitService.delFruit(fid);
            //response.sendRedirect("fruit.do");
            return "redirect:fruit.do";
        }
        return "error";
    }

    private String add(String fname,Integer price,Integer fcount,String remark,HttpServletRequest request) {
        Fruit fruit = new Fruit(0,fname,price,fcount,remark);
        fruitService.addFruit(fruit);
        //response.sendRedirect("fruit.do");
        return "redirect:fruit.do";
    }

    private String index(String oper,String keyword,Integer pageNo,HttpServletRequest request) {
        HttpSession session = request.getSession();

        if(pageNo==null){
            pageNo=1;
        }
        if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
            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);


        List<Fruit> fruitList = fruitService.getFruitList(keyword,pageNo);
        System.out.println(fruitList);
        session.setAttribute("fruitList",fruitList);

        //总记录条数
        int pageCount=fruitService.getPageCount(keyword);

        session.setAttribute("pageCount",pageCount);

        return "index";
    }
}

7.MVC-IOC实现

(1)耦合/依赖

依赖指的是某某某离不开某某某
在软件系统中,层与层之间是存在依赖的。我们也称之为耦合。
我们系统架构或者是设计的一个原则是: 高内聚低耦合。
层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合(就是没有耦合)

(2)IOC - 控制反转 / DI - 依赖注入

1.控制反转:
1) 之前在Servlet中,我们创建service对象 ,
	FruitService fruitService = new FruitServiceImpl();
	这句话如果出现在servlet中的某个方法内部,那么这个fruitService的作用域(生命周期)应该就是这个方法级别;
	如果这句话出现在servlet的类中,也就是说fruitService是一个成员变量,那么这个fruitService的作用域(生命周期)应该就是这个servlet实例级别

2) 之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML,产fruitService实例,存放在beanMap中,这个beanMap在一个BeanFactory中因此,我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转

2.依赖注入:
1) 之前我们在控制层出现代码:FruitService fruitService = new FruitServiceImpl();那么,控制层和service层存在耦合。
2) 之后,我们将代码修改成FruitService fruitService = null ;
然后,在配置文件中配置:
<bean id="fruit" class="FruitController">
	<property name="fruitService" ref="fruitService"/>
</bean>

(3)创建BeanFactory

BeanFactory .java

package com.atguigu.myssm.io;

/**
 * @author ZKYAAA
 * @create 2022-04-19 23:28
 */
public interface BeanFactory {
    Object getBean(String id);
}

ClassPathXmlApplicationContext.java

package com.atguigu.myssm.io;

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.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.util.HashMap;
import java.util.Map;

/**
 * @author ZKYAAA
 * @create 2022-04-19 23:29
 */
public class ClassPathXmlApplicationContext implements BeanFactory{
    private Map<String,Object> beanMap=new HashMap<>();

    public ClassPathXmlApplicationContext(){
        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;
                    // <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
                    String beanId = beanElement.getAttribute("id");
                    String className=beanElement.getAttribute("class");
                    Class beanClass = Class.forName(className);
                    //创建bean实例
                    Object beanObj = beanClass.newInstance();
                    //将nean实例对象保存到map容器中
                    beanMap.put(beanId,beanObj);
                    //到目前为止,此处需要注意的是,bean和bean之间的依赖关系还没有设置
                }
            }
            //5.组装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");
                    NodeList beanChildNodes = beanElement.getChildNodes();
                    for(int j=0;j<beanChildNodes.getLength();j++){
                        Node beanChildNode = beanChildNodes.item(j);
                        if(beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){
                            Element propertyElement =(Element)beanChildNode;
                            String propertyName = propertyElement.getAttribute("name");
                            String propertyRef= propertyElement.getAttribute("ref");
                            //1)找到propertyRef对应的实例
                            Object refObj = beanMap.get(propertyRef);
                            //2)将refObj设置到当前bean对应的实例的property属性上去
                            Object beanObj = beanMap.get(beanId);
                            Class baenClass = beanObj.getClass();
                            Field propertyfield = baenClass.getDeclaredField(propertyName);
                            propertyfield.setAccessible(true);
                            propertyfield.set(beanObj,refObj);
                        }
                    }
                }
            }
        } 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();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }


    @Override
    public Object getBean(String id) {
        return beanMap.get(id);
    }
}

DispatcherServlet.java修改

package com.atguigu.myssm.myspringmvc;

import com.atguigu.myssm.io.BeanFactory;
import com.atguigu.myssm.io.ClassPathXmlApplicationContext;
import com.atguigu.myssm.util.StringUtil;
import com.mysql.cj.ServerPreparedQuery;
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.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ZKYAAA
 * @create 2022-04-18 15:19
 */
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet{
    private BeanFactory beanFactory;

    //解析配置文件
    public DispatcherServlet(){

    }

    public void init() throws ServletException {
        super.init();
        beanFactory=new ClassPathXmlApplicationContext();
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置编码
        request.setCharacterEncoding("utf-8");

        //假设url是:http://localhost:8088/pro15/hello.do
        //那么servletPath是:HelloController
        //hello.do--->hello--->HelloController
        /*思路:
        第一步:/hello.do 字符串处理得到hello
        第二步:hello->HelloController 或者 fruit->FruitController
         */
        String servletPath = request.getServletPath(); // 字符串/hello.do
        System.out.println("servletPath="+servletPath);
        servletPath = servletPath.substring(1); //hello.do
        int lastDoIndex=servletPath.lastIndexOf(".do");
        servletPath=servletPath.substring(0,lastDoIndex); //hello
        System.out.println("servletPath1="+servletPath);

        //将hello--->HelloController两者对应起来
        Object controllerBeanObj = beanFactory.getBean(servletPath);

        //设置编码
        request.setCharacterEncoding("utf-8");
        String operate = request.getParameter("operate");
        if(StringUtil.isEmpty(operate)){
            operate="index";
        }

        try {
            Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
            for(Method method:methods){
                if(operate.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);
                            String typeName = parameter.getType().getName();

                            Object parameterObj=parameterValue;
                            if(parameterObj!=null){
                                if("java.lang.Integer".equals(typeName)){
                                    parameterObj=Integer.parseInt(parameterValue);
                                }
                            }

                            parameterValues[i] = parameterObj;
                        }
                    }

                    //java.lang.IllegalAccessException:
                    // Class com.atguigu.myssm.myspringmvc.DispatcherServlet can not access a member of class com.atguigu.fruit.controllers.FruitController with modifiers "private"

                    //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());
                        response.sendRedirect(redirectStr);
                    }else {
                        super.processTemplate(methodReturnStr,request,response);//比如:"edit"
                    }

                }
            }

        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}


/*
http://localhost:8088/pro16/fruit.do?pageNo=2
常见错误:java.lang.IllegalArgumentException: argument type mismatch
参数类型不匹配

 */

六、过滤器Filter

1.过滤器Filter

1) Filter也属于Servlet规范
2) Filter开发步骤:新建类实现Filter接口,然后实现其中的三个方法:init、doFilter、destroy
   配置Filter,可以用注解@WebFilter,也可以使用xml文件 <filter> <filter-mapping>
3) Filter在配置时,和servlet一样,也可以配置通配符,例如 @WebFilter("*.do")表示拦截所有以.do结尾的请求
4) 过滤器链
   1)执行的顺序依次是: A B C demo03 C2 B2 A2
   2)如果采取的是注解的方式进行配置,那么过滤器链的拦截顺序是按照全类名的先后顺序排序的
   3)如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序

PS:报错(filter注解不对)将@WebFilter(“/.do")修改为@WebFilter("//do”)

Caused by: java.lang.IllegalArgumentException: Invalid <url-pattern> /*.do in filter mapping

七.事务管理

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值