5.Java Web之Servlet入门

1.AddServlet

AddServlet的功能:
1.获取用户(客户端)发送的数据
2.调用DAO中的方法完成添加功能
3.在控制台打印添加完成

1.1 接受客户端发送的信息

在webapp下创建“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>

创建java包下

package com.example.servlets;

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

public class AddServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String fname = req.getParameter("fname");
        String priceStr = req.getParameter("price");
        Integer price = Integer.parseInt(priceStr);
        String fcountStr = req.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        String remark = req.getParameter("remark");

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

在webapp/WEB-INF/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">
    <servlet>
        <servlet-name>AddServlet</servlet-name>
        <servlet-class>com.example.servlets.AddServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>AddServlet</servlet-name>
        <url-pattern>/add</url-pattern>
    </servlet-mapping>
    <!--
    1.用户发请求,action=add
    2.项目中,web.xml中找到url-pattern = /add  -> 12行
    3.找第11行的servlet-name = AddServlet
    4.找和servlet-mapping中servlet-name一直的servlet,找到第7行
    5.找到第8行servlet-class -> com.example.servlets.AddServlet
    6.用户发送的是post请求(method=post),因此tomcat会执行AddServlet中的doPost方法
    -->
</web-app>

运行html,在浏览器中打开http://localhost:8080/add.html
在网页中输入信息,然后添加,可以在idea中接受到信息

15-May-2022 14:45:29.776 信息 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory D:\Tomcat\webapps\manager has finished in 63 ms
fname = apple
price = 5
fcount = 50
remaker = OK

请添加图片描述

1.2 调用DAO中的方法完成添加功能

1)下载fruit并在com.example下导入(链接:https://pan.baidu.com/s/1-7PUdud7oDlmzlVLQLfR2w 提取码:wnki)

#mysql建表
CREATE DATABASE fruitdb CHARSET utf8;
USE fruitdb ;
CREATE TABLE `t_fruit` (
  `fid` INT(11) NOT NULL AUTO_INCREMENT,
  `fname` VARCHAR(20) NOT NULL,
  `price` INT(11) DEFAULT NULL,
  `fcount` INT(11) DEFAULT NULL,
  `remark` VARCHAR(50) DEFAULT NULL,
  PRIMARY KEY (`fid`)
) ENGINE=INNODB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;

INSERT  INTO `t_fruit`(`fid`,`fname`,`price`,`fcount`,`remark`) 
VALUES 
(1,'红富士',5,16,'红富士也是苹果!'),
(2,'大瓜',5,100,'王校长的瓜真香'),
(3,'南瓜',4,456,'水果真好吃'),
(4,'苦瓜',5,55,'苦瓜很好吃'),
(5,'莲雾',9,99,'莲雾是一种神奇的水果'),
(6,'羊角蜜',4,30,'羊角蜜是一种神奇的瓜'),
(7,'啃大瓜',13,123,'孤瓜');

2)修改AddServlet

package com.example.servlets;

import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.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;

public class AddServlet extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String fname = req.getParameter("fname");
        String priceStr = req.getParameter("price");
        Integer price = Integer.parseInt(priceStr);
        String fcountStr = req.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        String remark = req.getParameter("remark");

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

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

3)导入mysql的jar包
4)修改fruit/dao/base/BaseDao.java(根据自己的数据库更改信息)

5)然后点左上角的“File”接着点“Project Structure”,根据如下步骤选择“Artfacts”,删除原来的项目重新导入,然后确认
请添加图片描述

6)删除“target”文件夹后运行html,接着在网页上输入信息添加到数据库中

mysql> select * from t_fruit;
+-----+-----------+-------+--------+--------------------------------+
| fid | fname     | price | fcount | remark                         |
+-----+-----------+-------+--------+--------------------------------+
|   1 | 红富士    |     5 |     16 | 红富士也是苹果!                |
|   2 | 大瓜      |     5 |    100 | 王校长的瓜真香                 |
|   3 | 南瓜      |     4 |    456 | 水果真好吃                     |
|   4 | 苦瓜      |     5 |     55 | 苦瓜很好吃                     |
|   5 | 莲雾      |     9 |     99 | 莲雾是一种神奇的水果           |
|   6 | 羊角蜜    |     4 |     30 | 羊角蜜是一种神奇的瓜           |
|   7 | 啃大瓜    |    13 |    123 | 孤瓜                           |
|  38 | apple     |     5 |     50 | OK                             |
+-----+-----------+-------+--------+--------------------------------+
8 rows in set (0.00 sec)

2.中文乱码问题

tomcat8之前:
  1)get请求:
    //get方式目前不支持设置编码(基于tomcat8)
    //如果是get请求发送的中文数据,转码稍微有点麻烦(tomcat8之前)
    String fname = req.getParameter(“fname”);
    //1.将字符串打散成字节数组
    byte[] bytes = fname.getBytes(“ISO-8859-1”);
    //2.将字节数组按照设定的编码重新组装成字符串
    fname = new String(bytes,“utf-8”);
  2)post请求:
    req.setCharacterEncoding(“utf-8”);
tomcat8开始,设置编码,只需针对post方式
  req.setCharacterEncoding(“utf-8”);

注意:设置编码这一行代码必须在所有的获取参数动作之前

**get

如果添加中文时出现乱码

mysql> select * from t_fruit;
+-----+--------------------------------+-------+--------+--------------------------------------------------+
| fid | fname                          | price | fcount | remark                                           |
+-----+--------------------------------+-------+--------+--------------------------------------------------+
|   1 | 红富士                         |     5 |     16 | 红富士也是苹果!                                  |
|   2 | 大瓜                           |     5 |    100 | 王校长的瓜真香                                   |
|   3 | 南瓜                           |     4 |    456 | 水果真好吃                                       |
|   4 | 苦瓜                           |     5 |     55 | 苦瓜很好吃                                       |
|   5 | 莲雾                           |     9 |     99 | 莲雾是一种神奇的水果                             |
|   6 | 羊角蜜                         |     4 |     30 | 羊角蜜是一种神奇的瓜                             |
|   7 | 啃大瓜                         |    13 |    123 | 孤瓜                                             |
|  38 | apple                          |     5 |     50 | OK                                               |
|  39 | apple                          |     5 |     50 | OK                                               |
|  40 | �����                |     6 |     50 | �����好��                         |
+-----+--------------------------------+-------+--------+--------------------------------------------------+
10 rows in set (0.00 sec)

修改AddServlet(解决中文乱码问题)

package com.example.servlets;

import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.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;
import java.nio.charset.StandardCharsets;

public class AddServlet extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //post方式下,设置编码,防止乱码
        //注意:设置编码这一行代码必须在所有的获取参数动作之前
        req.setCharacterEncoding("utf-8");
        String fname = req.getParameter("fname");
        String priceStr = req.getParameter("price");
        Integer price = Integer.parseInt(priceStr);
        String fcountStr = req.getParameter("fcount");
        Integer fcount = Integer.parseInt(fcountStr);
        String remark = req.getParameter("remark");

        /*
        //get方式目前不支持设置编码(基于tomcat8)
        //如果是get请求发送的中文数据,转码稍微有点麻烦(tomcat8之前)
        String fname = req.getParameter("fname");
        //1.将字符串打散成字节数组
        byte[] bytes = fname.getBytes("ISO-8859-1");
        //2.将字节数组按照设定的编码重新组装成字符串
        fname = new String(bytes,"utf-8");
         */

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

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

结果正常

mysql> select * from t_fruit;
+-----+--------------------------------+-------+--------+--------------------------------------------------+
| fid | fname                          | price | fcount | remark                                           |
+-----+--------------------------------+-------+--------+--------------------------------------------------+
|   1 | 红富士                         |     5 |     16 | 红富士也是苹果!                                  |
|   2 | 大瓜                           |     5 |    100 | 王校长的瓜真香                                   |
|   3 | 南瓜                           |     4 |    456 | 水果真好吃                                       |
|   4 | 苦瓜                           |     5 |     55 | 苦瓜很好吃                                       |
|   5 | 莲雾                           |     9 |     99 | 莲雾是一种神奇的水果                             |
|   6 | 羊角蜜                         |     4 |     30 | 羊角蜜是一种神奇的瓜                             |
|   7 | 啃大瓜                         |    13 |    123 | 孤瓜                                             |
|  38 | apple                          |     5 |     50 | OK                                               |
|  39 | apple                          |     5 |     50 | OK                                               |
|  40 | �����                |     6 |     50 | �����好��                         |
|  41 | 阿克苏苹果                     |     6 |     50 | 阿克苏苹果好吃!                                 |
+-----+--------------------------------+-------+--------+--------------------------------------------------+
11 rows in set (0.00 sec)

3.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 destroy() 销毁方法

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")) {
            this.doPut(req, resp);
        }

3.在HttpServlet这个抽象类中,do方法都差不多

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_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开头的方法
name在HttpServlet中这些do方法默认都是405的实现风格——要我们子类去实现对应的方法,否则会报405错误

4.因此,我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法

4.Servlet的生命周期

1)生命周期:从出生到死亡的过程就是生命周期。对应servlet中的三个方法:init()、service()、destroy()
2)默认情况下:
第一次接受请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
从第二次请求开始,每一次都是服务
当容器关闭时,其中的所有servlet实例会被销毁,调用销毁方法(调用destroy())

3)通过案例发现:Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。
默认情况下,第一次请求时,tomcat才回去实例化,初始化,然后再服务
好处:提高系统的启动速度
缺点:第一次请求时,耗时较长
结论:如果需要提高系统的启动速度,当前默认情况即可。
如果需要提高响应速度,我们应该设置Servlet的初始化时机

4)Servlet的初始化时机
默认是第一次接收请求时,实例化,初始化
我们可以通过来设置Servlet启动的向后顺序,数字越小启动越靠前,最小值0

    <servlet>
        <servlet-name>Demo01Servlet</servlet-name>
        <servlet-class>com.example.servlets.Demo01Servlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>

5)Servlet在容器中是:单例的、线程不安全的
单例:所有的青后都是同一个实例去响应
线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程该改变了这个成员变量的值,导致第一个线程执行路径发生了变化
启发:尽量不要在Servlet中定义成员变量。如果不得不定义成员变量,那么:①不要去修改成员变量的值 ②不要根据成员变量的值做一些逻辑判断

创建Demo01Servlet类

package com.example.servlets;


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

public class Demo01Servlet extends HttpServlet {
    public Demo01Servlet(){
        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("正在销毁");
    }
}

修改web.xml

   <servlet>
        <servlet-name>Demo01Servlet</servlet-name>
        <servlet-class>com.example.servlets.Demo01Servlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo01Servlet</servlet-name>
        <url-pattern>/demo01</url-pattern>
    </servlet-mapping>

debug执行,然后在浏览器中输入:http://localhost:8080/demo01
多次刷新后可以在idea中看到:

正在实例化......
正在初始化......
正在服务......
正在服务......
正在服务......
正在服务......

然后容器关闭时销毁。

5.HTTP协议

1)HTTP(超文本传输协议)。HTTP最大的作用就是确定了请求和相应数据的格式。
2)HTTP是无状态的
3)HTTP请求响应包含两个部分:请求报文和响报文

5.1 请求报文

请求报文包含三个部分:请求行、请求消息头、请求主

请求行
作用:展示当前请求的最基本信息

GET /demo01 HTTP/1.1

请求方式
访问地址
HTTP协议的版本

请求消息头
作用:通过具体的参数对本次请求进行详细的说明
格式:键值对,键和值之间使用冒号隔开
相对比较重要的请求消息头

名称功能
Host服务器的主机地址
Accept声明当前请求能够接受的媒体类型
Referer当前请求来源页面的地址
Content-Length请求体内容的长度
Content-Type请求体内容类型,这一项的具体值是媒体类型中的某一种
Cookie浏览器访问服务器时携带的Cookie数据

请求体
作用:作为请求的主题,发送数据给服务器。具体来说其实就是POST请求方式的请求参数

格式:
①form data
含义:当前请求体是一个表单提交的请求参数
每一组请求参数是一个键值对
键和值中间是等号
键值对之间是&号

②Request Payload
含义:整个请求体以某种特定格式来住址数据,例如JSON格式

请求方式
HTTP1.1中共定义了八种请求方式:
GET:从服务器端获取数据,没有请求体,但是有一个queryString
POST:将数据保存到服务器端,有请求体,form data
PUT:命令服务器对数据执行更新
DELETE:命令服务器删除数据
HEAD
CONNECT
OPTIONS
TRACE

5.2 响应报文

响应报文包含三个部分:响应行、响应头、响应体

响应行

HTTP/1.1 200 OK

HTTP协议版本
响应状态码
响应状态的说明文字

响应消息头
响应体的说明书
服务端对浏览器设置数据,例如:服务器端返回Cookie信息

名称功能
Content-Type响应体的内容类型
Content-Length响应体的内容长度
Set-Cookie服务器返回新的Cookie信息给卢兰奇
location在重定向的情况下,告诉浏览器访问下一个资源的地址

响应体
服务器返回的数据主题,有可能是各种数据类型

HTML页面
图片
视频
以下载形式返回的文件
CSS文件
JavaScript文件

响应状态码
作用:以编码的形式告诉浏览器当前请求处理的结果

状态码含义
200服务器成功处理了当前请求,成功返回响应
302重定向
400[SpringMVC特定环境]请求参数问题
403没有权限
404找不到目标资源
405请求方式和服务器端对应的处理方式不一致
406[SpringMVC特定环境]请求扩展名和实际返回的响应体类型不一致
50X服务器端内部错误,通常都是服务器端抛异常了

404产生的具体原因:

  • 访问地址写错了,确实是没有这个资源
  • 访问了WEB-INF目录下的资源
  • Web应用启动的时候,控制台已经抛出异常,导致整个Web应用不可用,访问任何资源都是404
  • 服务器端缓存

6.会话(session)

1)Http是无状态的

  • 服务器无法判断多个请求是同一个客户端发送的,还是不同客户端发送的
  • 无状态带来的显示问题:第一次请求是添加商品到购物车,第二次请求是结账;如果是两次请求服务器无法区分是同一个用户的,那么就会混乱。
  • 通过会话跟踪技术来解决无状态的问题

创建类

package com.example.servlets;

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

public class Demo02Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取session,如果获取不到,则创建一个新的
        HttpSession session = req.getSession();
        System.out.println("session ID:" + session.getId());
    }
}

修改web.xml

    <servlet>
        <servlet-name>Demo02Servlet</servlet-name>
        <servlet-class>com.example.servlets.Demo02Servlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo02Servlet</servlet-name>
        <url-pattern>/demo02</url-pattern>
    </servlet-mapping>

开启容器,然后在浏览器中多次访问:http://localhost:8080/demo02
idea中显示

session ID:9FBEB916D0FB96DBB44234F3D55A70B0
session ID:9FBEB916D0FB96DBB44234F3D55A70B0

2)会话跟踪技术
客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
下一次客户端给服务器发请求时,会把sessionID发送给服务器,从而确认发送请求的客户端

常用的API:
request.getSession() -> 获取当前的会话,没有则创建一个新的会话
request.getSession(true) -> 效果和不带参数一样
request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的

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

3)session保存作用域
session保存作用域是和具体的某个session对应的
常用的API:
void session.setAttribute(k,v)
Object session.getAttribute(k)
void removeAttribute(k)

request.getSession()
向当前session保存作用域保存一个数据“lina”,对应的key为“uname”
session.setAttribute("uname","lina");

从当前session保存作用域取指定的key,可就是uname,对应的value值
session.getAttribute("uname");

实验
①创建类,向HttpSession保存数据

package com.example.servlets;

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

//演示向HttpSession保存数据
public class Demo03Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getSession().setAttribute("uname","lina");
    }
}

②创建类获取unname

package com.example.servlets;

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

//获取unname
public class Demo04Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object uname = req.getSession().getAttribute("uname");
        System.out.println(uname);
    }
}

③配置web.xml

    <servlet>
        <servlet-name>Demo03Servlet</servlet-name>
        <servlet-class>com.example.servlets.Demo03Servlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo03Servlet</servlet-name>
        <url-pattern>/demo03</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>Demo04Servlet</servlet-name>
        <servlet-class>com.example.servlets.Demo04Servlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo04Servlet</servlet-name>
        <url-pattern>/demo04</url-pattern>
    </servlet-mapping>

④重启容器,然后访问:http://localhost:8080/demo03
接着在同一浏览器访问:http://localhost:8080/demo04
在idea中返回:lina

⑤在其他浏览器中访问:http://localhost:8080/demo04
返回:null

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

1)服务器内部转发
request.getRequestDispatcher(“…”).forward(request,response);
一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
地址栏没有变化

2)客户端重定向
response.sendRedirect(“…”);
两次请求响应的过程。客户端知道请求url有变化
地址栏有变化

实验

①创建类Demo05Servlet演示服务器端内部转发

package com.example.servlets;

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

//演示服务器端内部转发以及客户端重定向
public class Demo05Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo05....");
        //服务器端内部转发
        req.getRequestDispatcher("demo06").forward(req,resp);
    }
}

②创建类Demo06Servlet

package com.example.servlets;

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

public class Demo06Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo06...");
    }
}

③配置web.xml

    <servlet>
        <servlet-name>Demo05Servlet</servlet-name>
        <servlet-class>com.example.servlets.Demo05Servlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo05Servlet</servlet-name>
        <url-pattern>/demo05</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>Demo06Servlet</servlet-name>
        <servlet-class>com.example.servlets.Demo06Servlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo06Servlet</servlet-name>
        <url-pattern>/demo06</url-pattern>
    </servlet-mapping>

④浏览器访问:http://localhost:8080/demo05
idea显示:
demo05…
demo06…

浏览器地址依然是:http://localhost:8080/demo05
实际上显示的已经是demo06页面

⑤Demo05Servlet演示客户端重定向

package com.example.servlets;

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

//演示服务器端内部转发以及客户端重定向
public class Demo05Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("demo05....");
        //服务器端内部转发
//        req.getRequestDispatcher("demo06").forward(req,resp);
        //客户端重定向
        resp.sendRedirect("demo06");
    }
}

⑥访问:http://localhost:8080/demo05
idea显示:
demo05…
demo06…

浏览器地址变为:http://localhost:8080/demo06

7.Thymeleaf(视图模板技术)

1)添加thymeleaf的jar包(链接:https://pan.baidu.com/s/1sZGhKYLD_8VKkOBS233q5A 提取码:aqxd)
2)新建一个Servlet类ViewBassServlet
3)在web.xml文件中添加配置
配置前缀view-prefix
配置后缀view-suffix
4)使得我们的Servlet继承ViewBaseServlet
5)根据逻辑视图明层得到物理视图名称
//此处的视图名称是index
//thymeleaf会将这个 逻辑视图名称 对应的 物理视图 名称上去
//逻辑视图名称:index
//物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
//所以真实的视图名称是:/ index .html
super.processTemplate(“index”,req,resp);

6)使用thymeleaf的标签
th:if, th:unless, th:each, th:text

实验
①新建一个Servlet类ViewBassServlet

package com.example.servlets;

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;

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文件中添加配置

    <!--配置上下文参数-->
    <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>

③ 使得我们的Servlet继承ViewBaseServlet

package com.example.servlets;

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

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;

//Servlet从3.0版本开始支持注解方式的注册
@WebServlet("/index")
public class IndexServlet extends ViewBaseServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        FruitDAO fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitDAO.getFruitList();
        //保存到session作用域
        HttpSession session = req.getSession();
        session.setAttribute("fruitList",fruitList);

        //此处的视图名称是index
        //thymeleaf会将这个 逻辑视图名称 对应的 物理视图 名称上去
        //逻辑视图名称:index
        //物理视图名称:view-prefix + 逻辑视图名称 + view-suffix
        //所以真实的视图名称是:/ index .html
        super.processTemplate("index",req,resp);
    }
}

④ 在webapp/css目录下新建index.css

*{
    color: threeddarkshadow;
}
body{
    margin:0;
    padding:0;
    background-color:#808080;
}
div{
    position:relative;
    float:left;
}

#div_container{
    width:80%;
    height:100%;
    border:0px solid blue;
    margin-left:10%;
    float:left;
    background-color: honeydew;
    border-radius:8px;
}
#div_fruit_list{
    width:100%;
    border:0px solid red;
}
#tbl_fruit{
    width:60%;
    line-height:28px;
    margin-top:16px;
    margin-left:20%;
}
#tbl_fruit , #tbl_fruit tr , #tbl_fruit th , #tbl_fruit td{
    border:1px solid gray;
    border-collapse:collapse;
    text-align:center;
    font-size:16px;
    font-family:"黑体";
    font-weight:lighter;

}
.w20{
    width:20%;
}
.delImg{
    width:24px;
    height:24px;
}
.btn{
    border:1px solid lightgray;
    width:80px;
    height:24px;
}

.center{
    text-align:center;
}
.f30{
    font-size: 30px;
}

⑤ 在webapp下新建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}"></td>
                <td th:text="${fruit.fcount}"></td>
                <td><img src="imgs/del.jpg" class="delImg"/></td>
            </tr>
        </table>
    </div>
</div>
</body>
</html>

⑥ 浏览器访问:http://localhost:8080/index
图片可以使用前面fruit的图片

8.保存作用域

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

8.1 request(一次请求响应范围)

实验
① 保存数据到保存作用域

package com.example.servlet2;

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 java.io.IOException;

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

② 获取保存作用域的数据

package com.example.servlet2;

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 java.io.IOException;

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

    }
}

③ 访问:http://localhost:8080/demo01
idea显示:null
地址自动跳转到demo02,demo02无法获取demo01保存作用域的数据

④ 修改demo01Servlet

package com.example.servlet2;

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 java.io.IOException;

@WebServlet("/demo01")
public class Demo01Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.向request保存作用域保存数据
        req.setAttribute("uname","lili");
        //2.客户端重定向
//        resp.sendRedirect("demo02");
        //3.服务器端转发
        req.getRequestDispatcher("demo02").forward(req,resp);
    }
}

⑤ 访问:http://localhost:8080/demo01
idea显示:lili
访问地址认为demo01,而且获取到保存作用域的数据

8.2 session(一次会话范围)

实验
演示session保存作用域

① Demo03Servlet

package com.example.servlet2;

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 java.io.IOException;

//演示session保存作用域
@WebServlet("/demo03")
public class Demo03Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.向session保存作用域保存数据
        req.getSession().setAttribute("uname","lili");
        //2.客户端重定向
        resp.sendRedirect("demo04");
        //3.服务器端转发
//        req.getRequestDispatcher("demo04").forward(req,resp);
    }
}

② Demo04Servlet

package com.example.servlet2;

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 java.io.IOException;

@WebServlet("/demo04")
public class Demo04Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取session保存作用域保存的数据,key为uname
        Object uname = req.getSession().getAttribute("uname");
        System.out.println(uname);

    }
}

③ 访问:http://localhost:8080/demo03
可以获取到保存作用域的数据

8.3 application(整个应用程序范围)

实验
① Demo05Servlet

package com.example.servlet2;

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 java.io.IOException;

//演示application保存作用域
@WebServlet("/demo05")
public class Demo05Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.向application保存作用域保存数据
        //ServletContext:Servlet上下文
        ServletContext application = req.getServletContext();
        application.setAttribute("uname","lili");
        //2.客户端重定向
//        resp.sendRedirect("demo06");
        //3.服务器端转发
//        req.getRequestDispatcher("demo04").forward(req,resp);
    }
}

② Demo06Servlet

package com.example.servlet2;

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 java.io.IOException;

@WebServlet("/demo06")
public class Demo06Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.获取application保存作用域保存的数据,key为uname
        ServletContext apllication = req.getServletContext();
        Object uname = apllication.getAttribute("uname");
        System.out.println(uname);
    }
}

③ 在浏览器中访问:http://localhost:8080/demo05
在另一种浏览器中访问:http://localhost:8080/demo06
能够获取到lili
说明保存作用域范围是整个应用程序范围

9.路径问题

1)相对路径
2)绝对路径

<base href="http://localhost:8080/" /> 的作用是当前页面上的所有的路径以这个为基础
<link href="css/shopping.css">

在Thymeleaf中(下面的@表示根目录)
th:href="@{}"
<link th:href="@{css/shopping.css}">
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值