JavaWeb--servlet详解

Servlet详解

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

1、Servlet规范

  1. servlet规范来自于JAVAEE规范中的一种
  2. 作用:
  • 在servlet规范中,指定【动态资源文件】开发步骤
  • 在servlet规范中,指定http服务器调用动态资源文件规范
  • 在servlet规范中,指定htpp服务器管理动态资源文件实例对象规范

2、servlet接口实现类

  1. servlet接口来自于servlet规范下的接口,这个接口存在Http服务器提供的jar包

  2. Tomcat服务器下lib文件有一个servlet-api.jar存放servlet接口(javax.servlet.Servlet接口)

    61

  3. servlet规范中任务,Http服务器能调用的【动态资源文件】必须是一个servlet接口实现类

1
2
3
4
5
6
7
8
9
#例子
class Student{
//不是动态资源文件,Tomcat无权调用
}
class Teacher implements Servlet{
//合法动态资源文件,Tomcat有权调用
Servlet odj = new Teacher();
obj.doGet();
}

3、servlet接口实现类开发步骤

  1. 第一步:创建一个java类继承于HttpServlet父类,使之成为一个Servlet接口实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @author tianzedeng
* @date 2022/3/18 - 9:54
* 子类->父类->A接口
* 此时子类也是A接口实现类
* 若子类直接实现A接口,需要重写所有方法
* 抽象类作用:降低接口实现类对接口实现过程中的难度
* 将接口中不需要使用抽象方法交给抽象类进行完成
* 这样接口实现类只需要对接口需要的方法进行重写
* servlet接口中的方法:
* init()
* getConfig()
* service()//仅仅需要使用此方法
* getServletInfo
* destroy()
* Tomcat根据Servlet规范调用Servlet接口实现类规则:
* 1.Tomcat有权创建Servlet接口实现类实例对象
* Servlet oneServlet = new OneServlet();
* 2.Tomcat根据实例对象调用service方法处理当前请求
* oneServlet.service();
* oneServlet----extends--->(abstract)HttpServlet----extends---> (abstract)GenericServlet----implements--->Servlet接口
*/

67

2.第二步:重写HttpServlet父类两个方法。doGet或doPost。

浏览器——get方式—->oneServlet.doGet()

浏览器——post方式—->oneServlet.doPost()

1
2
3
4
5
6
7
8
9
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("oneServlet类针对浏览器发送GET请求方式处理");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("oneServlet类针对浏览器发送POST请求方式处理");
}

调用方式:

1
2
3
4
5
6
7
8
9
10
11
/*伪代码*/
HttpServlet:service(){
if(请求方式 == GET){
this.doGet
}else if(请求方式 == POST){
this.doPost
}
}
OneServlet:doGet doPost
Servlet oneServlet = new OneServlet();
oneServlet.service();

总结:

​ 1.重写规则:

访问修饰符的限制一定要不小于被重写方法的访问修饰符

比如:Object类有个toString()方法,开始重写这个方法的时候我们总容易忘记Public修饰符,出错的原因就是:没有加任何访问修饰符的方法具有包访问权限,Default访问权限小于Public访问权限,所以编译器出错。

参数列表必须与被重写方法的相同。

重写的方法的返回值必须和被重写的方法的返回一致或者兼容;

重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类;

被重写的方法不能为private,子类再写一个同名的方法并不是对父类方法进行重写(Override),而是重新生成一个新的方法;

静态方法不能被重写。

不能重写被标识为final的方法,子类中必须重写父类中的abstract方法 。

​ 2.抽象类作用:

​ 3.子类实现接口规则:

​ 4.this指向:

​ 5.继承规则:

3、第三步:将Servlet接口实现类信息【注册】到Tomcat服务器中

【网站】-【web】-【WEB-INF】-web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 将Servlet接口实现类类路径地址交给Tomcat
<servlet>
<servlet-name>tzd</servlet-name>#声明一个变量存储servlet接口实现类类路径
<servlet-class>com.tzd.controller.OneServlet</servlet-class>#声明servlet接口实现类
</servlet>

Tomcat String tzd = "com.tzd.controller.OneServlet";

#为了降低用户访问Servlet接口实现类难度,需要设置简短请求别名
<servlet-mapping>
<servlet-name>tzd</servlet-name>
<url-pattern>/one</url-pattern>
</servlet-mapping>

#访问地址:http://localhost:8080/myweb/one/

4、servlet对象生命周期

​ 1、网站中所有的servlet接口实现类的实例对象,只能由http服务器负责创建。开发人员不能手动创建servlet接口实现类的实例对象。

​ 2、在默认情况下,http服务器接收到对于当前servlet接口实现类第一次请求时自动创建这个servlet接口实现类的实例对象。

​ 在手动配置情况下,要求http服务器在启动时自动创建某个servlet接口实现类的实例对象。

1
2
3
4
5
<servlet>
<servlet-name>tzd</servlet-name> #声明一个变量存储servlet接口实现类类路径
<servlet-class>com.tzd.controller.OneServlet</servlet-class> #声明servlet接口实现类
<load-on-startup>1</load-on-startup> #填写大于0的证书
</servlet>

​ 3、在http服务器运行期间,一个servlet接口实现类只能被创建出一个实例对象。

​ 4、在http服务器关闭时刻,自动将网站中所有的servlet对象进行销毁。

5、HttpServletResponse接口

介绍

  1. HttpServletResponse接口来自于Servlet规范中,在Tomcat中存在servlet-api.jar。
  2. HTTPServletResponse接口实现类由Http服务器提供。
  3. HTTPServletResponse接口负责将doGet/doPost方法执行结果写入到响应体交给浏览器
  4. 开发人员习惯于将HttpServletResponse接口修饰的对象称为响应对象

主要功能

  1. 将执行结果以二进制形式写入响应体
  2. 设置响应头中content-type属性值,从而控制浏览器使用对应编译器将响应体二进制数据编译为文字、图片、视频、命令
  3. 设置响应头中location属性,将一个请求地址赋值为location,从而控制浏览器向指定服务器发送请求
1
2
3
4
5
6
7
#输入:97 输出:a
#原因:out.write方法可以将字符、字符串、ASCII码写入响应体
int result = 97;

PrintWriter writer = response.getWriter();
//writer.write(result);
writer.print(result);
1
2
3
4
5
6
7
8
9
String result = "java<br>html<br>";
String str = "红烧排骨<br>猪肉炖粉条<br>";
//设置响应头content-type
response.setContentType("text/html;charset=utf-8");
//向Tomcat索要输出流
PrintWriter writer = response.getWriter();
//通过输出流将结果写入响应体
writer.print(result);
writer.print(str);

6、HttpServletRequest接口

介绍

  1. HttpServletRequest接口来自于Servlet规范中,在Tomcat中存在servlet-api.jar。
  2. HTTPServletRequest接口实现类由Http服务器提供。
  3. HTTPServletRequest接口负责将doGet/doPost方法运行时读取http请求协议包中信息。
  4. 开发人员习惯于将HTTPServletRequest接口修饰的对象称为请求对象

作用

  1. 可以读取Http请求协议包中请求行信息
  2. 可以读取保存在Http请求协议中请求头或者请求体中请求参数信息
  3. 可以代替浏览器向Http服务器申请资源文件调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
*问题:
* 以get方式发送中文参数内容时,得到正常结果
* 以post方式发送中文参数时,出现乱码
* 原因:
* 浏览器以get方式发送请求时,请求参数保存在请求头,在http请求协议包到达http服务器之后,首先就是解码
* 请求头二进制内容由tomcat负责解码,默认是utf-8字符集
*
* 浏览器以post方式发送请求时,请求参数保存在请求体
* 请求体二进制内容是由当前的请求对象request负责解码。默认使用ISO-8858-1字符集
*
* 解决方案:
*在post请求方式下,读取请求体的内容之前,应该通知请求对象使用utf-8字符集对请求体内容进行一次重新解码
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//通知请求对象,用utf-8字符集对请求体内容进行一次重新解码
request.setCharacterEncoding("utf-8");
//通过请求对象,读取请求体参数信息
String value = request.getParameter("userName");
System.out.println("从请求体得到的参数值"+value);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//通过请求对象,读取请求头的参数信息
String userName = request.getParameter("userName");
System.out.println("从请求头得到的参数值"+userName);
}

7、请求对象和响应对象生命周期

68

  1. 在http服务器接收到浏览器发送的http请求协议包之后,自动为当前的http请求协议包生成一个请求对象和一个响应对象

  2. 在http服务器调用doGet/doPost方法时,负责将请求对象响应对象作为实参传递到方法中,确保方法正确执行。

  3. 在http服务器准备推送响应协议包之前,负责将本次请求关联的请求对象响应对象销毁。

    请求对象响应对象生命周期贯穿一次请求的处理过程中

69

7、用户信息注册流程

70

user_add.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户信息注册</title>
</head>
<body>
<center>
<form action="/myweb/user/add" method="get">
<table border="2">
<tr>
<td>用户姓名:</td>
<td><input type="text" name="userName"/></td>
</tr>
<tr>
<td>用户密码:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td>用户性别:</td>
<td>
<input type="radio" name="sex" value="男"/>
<input type="radio" name="sex" value="女"/>
</td>
</tr>
<tr>
<td>用户邮箱:</td>
<td><input type="text" name="email"/></td>
</tr>
<tr>
<td><input type="submit" value="用户注册"/></td>
<td><input type="reset" value="重置"/></td>
</tr>
</table>
</form>
</center>
</body>
</html>

实体类Users

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.tzd.entity;

/**
* @author tianzedeng
* @date 2022/3/24 - 20:20
*/
public class Users {

private Integer userId;
private String userName;
private String password;
private String sex;
private String email;

public Integer getUserId() {
return userId;
}

public void setUserId(Integer userId) {
this.userId = userId;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public Users() {
}

public Users(Integer userId, String userName, String password, String sex, String email) {
this.userId = userId;
this.userName = userName;
this.password = password;
this.sex = sex;
this.email = email;
}

}

JDBC封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package com.tzd.util;

import javax.swing.plaf.PanelUI;
import java.sql.*;

/**
* @author tianzedeng
* @date 2022/3/24 - 20:25
*/
public class JdbcUtil {

public static final String URL = "jdbc:mysql://localhost:3306/servlet";
public static final String USERNAME = "root";
public static final String PASSWORD = "root";
public static PreparedStatement preparedStatement = null;
public static Connection connection = null;

//将jar包中driver实现类加载到jvm中
static {
try{
Class.forName("com.mysql.jdbc.Driver");
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}

//封装连接通道创建细节
public static Connection getConnection(){
try {
connection = DriverManager.getConnection(URL,USERNAME,PASSWORD);
}catch (SQLException e){
e.printStackTrace();
}
return connection;
}

//封装交通工具创建细节
public static PreparedStatement createStatement(String sql){
try {
preparedStatement = getConnection().prepareStatement(sql);
}catch (SQLException e){
e.printStackTrace();
}
return preparedStatement;
}

//preparedStatement和connection
public static void close(){
if (preparedStatement!=null){
try{
preparedStatement.close();
}catch (SQLException e){
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
public static void close(ResultSet resultSet){
if (resultSet!=null){
try {
resultSet.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}

数据持久层UserDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.tzd.dao;

import com.tzd.entity.Users;
import com.tzd.util.JdbcUtil;

import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
* @author tianzedeng
* @date 2022/3/25 - 13:59
*/
public class UserDao {

JdbcUtil jdbcUtil = new JdbcUtil();
//用户注册
public int add(Users users){
String sql = "insert into users(userName,password,sex,email) values (?,?,?,?)";
PreparedStatement preparedStatement = jdbcUtil.createStatement(sql);
int result = 0;
try{
preparedStatement.setString(1,users.getUserName());
preparedStatement.setString(2,users.getPassword());
preparedStatement.setString(3,users.getSex());
preparedStatement.setString(4,users.getEmail());
result =preparedStatement.executeUpdate();
}catch (SQLException e){
e.printStackTrace();
}finally {
jdbcUtil.close();
}
return result;
}
}

数据访问UserAddServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.tzd.controller;

import com.tzd.dao.UserDao;
import com.tzd.entity.Users;

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.io.PrintWriter;

/**
* @author tianzedeng
* @date 2022/3/25 - 10:50
*/
public class UserAddServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userName,password,sex,email;
UserDao userDao = new UserDao();
Users users = null;
int result = 0;
PrintWriter printWriter = null;

//1.调用请求对象 读取请求头里面的参数信息 得到用户注册信息
userName = request.getParameter("userName");
password = request.getParameter("password");
sex = request.getParameter("sex");
email = request.getParameter("email");

//2.调用UserDao将用户信息填充到Insert命令中并借助JDBC规范发送到数据库中
users = new Users(null,userName,password,sex,email);
result = userDao.add(users);

//3.调用响应对象将处理结果以二进制形式写入响应体
response.setContentType("text/html;charset=utf-8");
printWriter = response.getWriter();
if (result == 1){
printWriter.print("<font style='color:red;font-size:40'>用户注册成功</font>");
}else {
printWriter.print("<font style='color:red;font-size:40'>用户注册失败</font>");
}
}
//Tomcat负责销毁请求对象和响应对象
//Tomcat负责将http响应协议包推送到发起请求的浏览器上
//浏览器根据响应头conten-type指定编译器对响应体二进制内容编辑
//浏览器将编辑后结果在窗口中展示给用户
}

8、用户查询信息

71

在UserDao中创建FindAll()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public List findAll(){
String sql = "select * from users";
PreparedStatement preparedStatement = jdbcUtil.createStatement(sql);
ResultSet resultSet;
List userList = new ArrayList();
try {
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
Integer userId = resultSet.getInt("userId");
String userName = resultSet.getString("userName");
String password = resultSet.getString("password");
String sex = resultSet.getString("sex");
String email = resultSet.getString("email");
Users users = new Users(userId,userName,password,sex,email);
userList.add(users);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
jdbcUtil.close();
}
return userList;
}

新建UserFindServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.tzd.controller;

import com.tzd.dao.UserDao;
import com.tzd.entity.Users;

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.io.PrintWriter;
import java.util.List;

/**
* @author tianzedeng
* @date 2022/3/25 - 15:01
*/
public class UserFindServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
UserDao userDao = new UserDao();
PrintWriter printWriter;
//调用Dao将查询命令推送到数据库服务器上,得到所有用户信息【List】
List<Users> userList = userDao.findAll();
//调用响应对象,将用户信息结合<table>标签命令以二进制形式写入响应体
response.setContentType("text/html;charset=utf-8");
printWriter = response.getWriter();
printWriter.print("<table border='2' align='center'>");
printWriter.print("<tr>");
printWriter.print("<td>用户编号</td>");
printWriter.print("<td>用户姓名</td>");
printWriter.print("<td>用户密码</td>");
printWriter.print("<td>用户性别</td>");
printWriter.print("<td>用户邮箱</td>");
printWriter.print("</tr>");
for (Users users : userList) {
printWriter.print("<tr>");
printWriter.print("<td>" + users.getUserId() + "</td>");
printWriter.print("<td>" + users.getUserName() + "</td>");
printWriter.print("<td>******</td>");
printWriter.print("<td>" + users.getSex() + "</td>");
printWriter.print("<td>" + users.getEmail() + "</td>");
printWriter.print("</tr>");
}
printWriter.print("</table>");
}
}

9、登录验证

72

login.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<center>
<font style="color: red;font-size: 30px">登录信息不存在,请重新登录</font>
<form action="/myweb/login" method="post">
<table border="2">
<tr>
<td>用户名</td>
<td><input type="text" name="userName"/></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td><input type="submit" value="登录"/></td>
<td><input type="reset"/></td>
</tr>
</table>
</form>

</center>
</body>
</html>

UserDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//登录验证
public int login(String userName,String password){
String sql ="select count(*) from users where userName=? and password=?";
PreparedStatement preparedStatement = JdbcUtil.createStatement(sql);
ResultSet resultSet = null;
int result = 0;
try {
preparedStatement.setString(1, userName);
preparedStatement.setString(2, password);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
result = resultSet.getInt("count(*)");
}
}catch (SQLException e){
e.printStackTrace();
}finally {
jdbcUtil.close();
}
return result;
}

LoginServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.tzd.controller;

import com.tzd.dao.UserDao;

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

/**
* @author tianzedeng
* @date 2022/3/28 - 10:26
*/
public class LoginServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userName,password;
UserDao userDao = new UserDao();
int result = 0;
//调用请求对象对请求体使用utf-8字符集进行重新编写
request.setCharacterEncoding("utf-8");
//调用请求对象读取请求参数体信息
userName = request.getParameter("userName");
password = request.getParameter("password");
//调用Dao将查询验证信息推送到数据库服务器上
result = userDao.login(userName,password);
//调用响应对象,根据验证结果将不同的资源文件地址写到响应头,返回给浏览器
if (result == 1){
response.sendRedirect("/myweb/index.html");
}else {
response.sendRedirect("/myweb/login_error.html");
}
}

}

10、资源文件

1、前提:用户可以记住网站名,但是不会记住网站资源文件

2、用户发送了一个针对某个网站的默认请求文件时,此时由http服务器自动从当前网站返回的资源文件

​ 正常请求:http://localhost:8080/myweb/index.html

​ 默认请求:http://localhost:8080/myweb/

3、Tomcat对于资源文件定位规则

  • 规则位置:Tomcat安装位置/conf/web.xml

  • 规则命令:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
      <!-- ==================== Default Welcome File List ===================== -->
    <!-- When a request URI refers to a directory, the default servlet looks -->
    <!-- for a "welcome file" within that directory and, if present, to the -->
    <!-- corresponding resource URI for display. -->
    <!-- If no welcome files are present, the default servlet either serves a -->
    <!-- directory listing (see default servlet configuration on how to -->
    <!-- customize) or returns a 404 status, depending on the value of the -->
    <!-- listings setting. -->
    <!-- -->
    <!-- If you define welcome files in your own application's web.xml -->
    <!-- deployment descriptor, that list *replaces* the list configured -->
    <!-- here, so be sure to include any of the default values that you wish -->
    <!-- to use within your application. -->
    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
  • 设置当前网络的默认欢迎资源文件规则

    规则位置:网站/web/WEB-INF/web.xml

    规则命令:

    1
    2
    3
    <welcome-file-list>
    <welcome-file>login.html</welcome-file>
    </welcome-file-list>

    网站设置自定义默认文件定位规则,此时Tomcat默认规则将失效。

11、http状态码

  1. 介绍

    • 由三位数字组成的一个符号
    • 由http服务器在推送响应包之前,根据本次请求处理情况,将http状态码写入到响应包中状态行上
    • 如果http服务器针对本次请求,返回了对应的资源文件。通过http状态码通知浏览器应该如何处理这个结果;如果http服务器针对本次请求,无法返回对应的资源文件,通过http状态码向浏览器解释不能提供的服务的原因
  2. 分类

    • 组成 100-599;分为5个大类

    • 1xx:最有特征的是100:通知浏览器本次返回的资源文件并不是一个独立的资源文件,需要浏览器在接收响应包之后,继续向http服务器索要依赖的其它资源文件。

      73

    • 2xx:最有特征的是200::通知浏览器本次返回的资源文件是一个完整独立的资源文件,浏览器在接收到之后,不需要索要其它关联文件。

    • 3xx:最有特征302:通知浏览器本次返回的不是一个资源文件内容而是一个资源文件地址,需要浏览器根据这个地址自动发起请求来索要这个资源文件。

      response.sendRedirect(“资源文件地址”)写入到响应头中location,而这个行为导致Tomcat将302状态码写入到状态行。

    • 4xx:404,通知浏览器,由于在服务端没有定位到被访问的资源文件因此无法提供帮助。

      ​ 405,通知到浏览器,在服务端已经定位到被访问的资源文件(Servlet),但是这个Servlet对于浏览器采用的请求方式不能处理。

    • 5xx:500,通知到浏览器,在服务端已经定位到被访问的资源文件(Servlet),这个servlet可以去接收浏览器的请求方式,但是Servlet在处理请求期间,由于JAVA异常导致处理失败。

12、多个servlet之间的调用规则

  1. 前提条件:

    某些来自于浏览器发送请求,往往需要服务端中多个servlet协同处理。但是浏览器一次只能访问一个servlet,导致用户需要手动通过浏览器发起多次请求才能得到服务。这样增加用户获得服务难度,导致用户放弃访问当前网站。

  2. 提高用户使用感受规则:

    无论本次请求涉及到多少个servlet,用户只需要手动通知浏览器发起一次请求即可。

  3. 实现方案:

    • 重定向解决方案
    • 请求转发解决方案

13、重定向解决方案

74

原理:用户第一次通过手动方式通知浏览器访问OneServlet。OneServlet工作完毕之后,将TwoServlet地址写入到响应头location属性中,导致Tomcat将302状态码写入到状态行。

在浏览器接收到响应包后,会读取302状态。此时浏览器自动根据响应头中location属性地址发起第二次请求,访问TwoServlet去完成请求中剩余任务。

实现命令:response.sendRedirect(“请求地址”),将地址写入响应包中的location属性中。

特征:

  1. 请求地址:既可以把当前网站内部的资源文件地址发送给浏览器,也可以把其它网站资源文件地址发送给浏览器。
  2. 请求次数:浏览器至少发送两次请求,但是只有第一次请求是用户手动发送。后续请求都是浏览器自动发送的。
  3. 请求方式:重定向解决方案中,通过地址栏通知浏览器发起下一次请求,因此通过重定向解决方案调用的资源文件接收的请求方式一定是GET。

缺点:

重定向解决方案需要在浏览器与服务器之间进行多次往返,大量时间消耗在往返次数上,增加用户等待服务时间。

14、请求转发方案

75

原理:用户第一次通过手动的方式要求浏览器去访问oneServlet,OneServlet工作完毕之后,通过当前的请求对象代替浏览器向Tomcat发送请求,申请调用TwoServlet。Tomcat在接收到这个请求之后,自动调用TwoServlet来完成剩余任务。

实现命令:请求对象代替浏览器向Tomcat发送请求。

1
2
3
4
//1.通过当前请求对象生成资源文件中申请发送报告
RequestDiaptcher requestDispatcher = request.getRequestDispatcher("/资源文件名");
//2.将报告对象发送给Tomcat
requestDispatcher.forward(当前的请求对象,当前的响应对象);

优点:

  1. 无论本次请求涉及到多少个Servlet,用户只需要手动通过浏览器发送一次请求。
  2. Servlet之间调用发生在服务端计算机上,节省服务器与浏览器之间往返次数,增加处理服务速度。

特征:

  1. 请求次数:在请求转发过程中,浏览器只发送一次请求。
  2. 请求地址:只能向Tomcat服务器申请调用当前网站下资源文件地址。
  3. 请求方式:在请求转发过程中,浏览器只发送了一个http请求包。参与本次请求的所有Servlet共享同一个请求包协议,因此这些Servlet接收的请求方式与浏览器发送的请求方式保持一致。

15、多个Servlet之间数据共享实现方案

数据共享:OneServlet工作完毕后,将产生数据交给TwoServlet来使用

Servlet规范中提供四种数据共享方案

  1. ServletContext接口
  2. Cookie接口
  3. HttpSession接口
  4. HttpServletRequest接口

16、ServletContext接口

介绍:

  1. 来自于Servlet规范中一个接口。在Tomcat中存在servlet-api.jar,在Tomcat中负责提供这个接口的实现类。
  2. 如果两个Servlet来自于同一个网站。彼此之间通过网站的ServletContext实例对象实现数据共享。
  3. 开发人员习惯于将ServletContext对象称为全局作用域对象。

工作原理:

76

每一个网站都存在一个全局作用域对象。在这个全局作用域对象相当于一个Map。在这个网站中OneServlet可以将一个数据存入到全局作用域对象,当前网站中其它Servlet此时都可以从全局作用域对象得到这个数据进行使用。

全局作用域对象的声明周期

  1. 在Http服务器启动过程中,自动为当前网站在内存中创建一个全局作用域对象。

  2. 在Http服务器运行期间,一个网站只有一个全局作用域对象。

  3. 在Http服务器运行期间,全局作用域对象一直处于存活状态。

  4. 正在Http服务器准备关闭时,负责将当前网站中全局作用域对象进行销毁处理。

    全局作用域对象生命周期贯穿网站整个运行期间。

命令的实现

同一个网站中,OneServlet将数据共享给TwoServlet。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//通过请求对象向Tomcat索要当前网站中全局作用域对象
ServletContext application = request.getServletContext();
//将数据添加到全局作用域对象作为共享数据
application.setAttribute("key1",data);
}
}

TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//通过请求对象向Tomcat索要当前网站中全局作用域对象
ServletContext application = request.getServletContext();
//从全局作用域对象中得到指定关键字对应数据
Object data = application.getAttribute("key1");
}
}

17、cookie接口

77

介绍

  1. cookie来自于Servlet规范中一个工具类,存在于Tomcat提供servlet-api.jar中。
  2. 如果两个Servlet来自于同一个网站。并且为同一个浏览器/用户提供服务,此时借助于cokkie对象进行数据共享。
  3. cookie存放当前用户的私人数据,在共享数据过程中提高服务质量。
  4. 在现实生活中,cookie相当于用户在服务端得到会员卡。

原理

  1. 用户通过浏览器第一次向网站发送请求是申请OneServlet。
  2. OneServlet在运行期间创建一个cookie存储与当前用户相关的数据。
  3. OneServlet工作完毕后,将cookie写入到响应头交还给当前浏览器。
  4. 浏览器收到响应包后,将cookie存储在浏览器的缓存中。
  5. 一段时间后,用户通过同一个浏览器再次向网站请求申请TwoServlet时。浏览器需要无条件的将网站推送过来的cookie写入到请求头中发送过去。
  6. 此时TwoServlet在运行时,就可以通过读取请求头中cookie中信息,得到OneServlet提供的共享数据。

实现命令

同一个OneServlet与TwoServlet借助于cookie实现数据共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.创建一个cookie对象,保存共享数据(当前用户数据)
//cookie相当于一个map,一个cookie中只能存放一个键值对,这个键值对的key和value只能是string类型,键值对中的key不能是中文
Cookie card = new Cookie("key1","abc");
Cookie card1 = new Cookie("key2","efg");
//2.将cookie写入到响应头,交给浏览器
response.addCookie(card);
response.addCookie(card2);
}
}
//浏览器或用户 <---------响应包 【200】
// 【cookie:key1=abc】
// 【】
// 【处理结果】

//浏览器向网站发送请求访问TwoServlet---->请求包【url:/myweb/two method:get】
// 【请求参数:xxxx
// cookie:key1=abc
// 】
// 【】
// 【】
TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.调用请求对象从请求头得到浏览器返回的cookie
Cookie cookieArray[] = request.getCookies();
//2.循环遍历数据得到每一个cookie的key和value
for(Cookie card:cookieArray){
String key = cookie.getName();
String value = card.getValue();
}
}
}

实战

78

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<center>
<font style="color: red;font-size: 40px">新会员申请开卡</font>
<form action="/cookie/one">
<table border="2">
<tr>
<td>用户名</td>
<td><input type="text" name="userName"/></td>
</tr>
<tr>
<td>预存金额</td>
<td><input type="text" name="money"/></td>
</tr>
<tr>
<td><input type="submit" value="申请开卡"/>></td>
<td><input type="reset"/></td>
</tr>
</table>
</form>
</center>
</body>
</html>

OneServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.tzd.controller;

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

/**
* @author tianzedeng
* @date 2022/3/28 - 20:41
*/
public class OneServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.调用请求对象读取请求头参数信息
String userName = request.getParameter("userName");
String money = request.getParameter("money");
//2.开卡
Cookie card1 = new Cookie("userName",userName);
Cookie card2 = new Cookie("money",money);
//3.发卡,将cookie写入到响应头中,交给浏览器
response.addCookie(card1);
response.addCookie(card2);
//4.通知tomcat将点餐页面内容写入到响应体交给浏览器(请求转发)
request.getRequestDispatcher("/index2.html").forward(request,response);
}
}

index2.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>点餐</title>
</head>
<body>
<center>
<font style="color: red;font-size: 40px">点餐页面</font>
<form action="/cookie/two">
食物类型:<input type="radio" name="food" value="饺子"/>饺子(30元)
<input type="radio" name="food" value="面条"/>面条(20元)
<input type="radio" name="food" value="盖饭"/>盖饭(15元)
<input type="submit" value="划卡消费"/>
</form>
</center>
</body>
</html>

TwoServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package com.tzd.controller;

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

/**
* @author tianzedeng
* @date 2022/3/28 - 20:53
*/
public class TwoServlet extends HttpServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int jiaozi_money = 30;
int noodle_money = 20;
int gaifan_money = 15;
String food;
String userName = null;
Integer money;
int consume=0,balance = 0;
Cookie cookArray[];
response.setContentType("text/html;charset=utf-8");
PrintWriter printWriter = response.getWriter();
Cookie newCard = null;
//1.读取请求头中的参数信息,得到用户点餐食物类型
food = request.getParameter("food");
//2.读取请求头中的cookie
cookArray = request.getCookies();
//3.刷卡消费
for (Cookie card:cookArray){
String key = card.getName();
String value = card.getValue();
if ("userName".equals(key)){
userName = value;
}else if("money".equals(key)){
money = Integer.valueOf(value);
if ("饺子".equals(food)){
if (jiaozi_money > money){
printWriter.print("用户"+userName+"余额不足,请充值");
}else {
newCard = new Cookie("money",(money-jiaozi_money)+"");
consume = jiaozi_money;
balance = money-jiaozi_money;
}
}else if ("面条".equals(food)){
if (noodle_money > money){
printWriter.print("用户"+userName+"余额不足,请充值");
}else {
newCard = new Cookie("money",(money-noodle_money)+"");
consume = noodle_money;
balance = money-noodle_money;
}
}else if ("盖饭".equals(food)){
if (gaifan_money > money){
printWriter.print("用户"+userName+"余额不足,请充值");
}else {
newCard = new Cookie("money",(money-gaifan_money)+"");
consume = gaifan_money;
balance = money-gaifan_money;
}
}
}
}
//4.将用户会员卡返还给用户
response.addCookie(newCard);
//5.将消费记录写到响应体中
printWriter.print("用户:"+userName+"本次消费:"+consume+"余额为:"+balance);
}
}

cookie销毁时机

  1. 在默认情况下,cookie对象存放在浏览器的缓存中。因此只要关闭浏览器,cookie对象就被销毁。

  2. 在手动设置情况下,可以要求浏览器将接收的cookie存放在客户端计算机的硬盘上,同时需要指定cookie在硬盘上的存活时间。在存活时间内范围内,关闭浏览器关闭客户端计算机,关闭服务器,都不会导致cookie被销毁。在存活时间到达时,cookie自动从硬盘上删除。

    1
    cookie.setMaxAge(60);

18、HttpSession接口

介绍

  1. HttpSession接口来自于Servlet规范下的一个接口。存在于Tomcat中servlet-api.jar,其实现类由Http服务器提供。Tomcat提供实现类存在于servlet-api.jar。
  2. 如果两个servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时借助于HttpSession对象进行数据共享。
  3. 开发人员习惯于将HttpSession接口修饰对象称为会话作用域对象。

HttpSession与Cookie区别

  1. 存储位置:cookie存放在客户端计算机中(浏览器内存/硬盘);HttpSession存放在服务端计算机内存。
  2. 数据类型:cookie对象存储共享数据类型只能是String;HttpSession对象可以存储任意类型的共享数据Object。
  3. 数据数量:一个cookie对象只能存储一个共享数据;HttpSession使用map集合存储共享数据,可以存储任意数量共享数据。
  4. 参照物:cookie相当于客户在服务端会员卡;HttpSession相当于客户在服务端的私人保险柜。

命令实现

同一个网站下OneServlet将数据传递给TwoServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OneServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.调用请求对象向Tomcat索要当前用户在服务器的私人储物柜
HttpSession session = request.getSession();
//2.将数据添加到用户私人储物柜
session.setAttribute("key1",共享数据);
}
}
//浏览器访问/myweb中TwoServlet
TwoServlet{
public void doGet(HttpServletRequest request,HttpServletResponse response){
//1.调用请求对象向Tomcat索要当前用户在服务器的私人储物柜
HttpSession session = request.getSession();
//2.从会话作用域对象得到OneServlet提供的共享数据
Object 共享数据 = session.getAttribute("key1");
}
}

模拟购物车

79

HttpSession与用户关联(cookie)

80

getSession()和getSession(false)

  1. getSession():如果当前用户在服务端已经拥有了自己的私人储物柜,要求tomcat将这个私人储物柜进行返回;如果当前用户在服务端尚未拥有自己的私人储物柜,要求tomcat为当前用户创建一个全新的私人储物柜。
  2. getSession(false):如果当前用户在服务端已经拥有了自己的私人储物柜,要求tomcat将这个私人储物柜进行返回;如果当前用户在服务端尚未拥有自己的私人储物柜,此时tomcat将返回null。

HttpSession销毁时机

  1. 用户与HttpSession关联时使用的cookie只能存放在浏览器缓存中。
  2. 在浏览器关闭时,意味着用户与其HttpSession关系被切断。
  3. 由于Tomcat无法监测浏览器何时关闭,因此在浏览器关闭时,并不会导致tomcat将浏览器关联的HttpSession进行销毁。
  4. 为了解决这个问题,tomcat为每个HttpSession对象设置空闲时间,这个空闲时间默认是30分钟。如果当前HttpSession对象空闲时间达到30分钟,此时tomcat认为用户已经放弃了自己的HttpSession,tomcat就会销毁这个HttpSession。

HttpSession空闲时间手动设置

在当前网站/web/WEB-INF/web.xml

1
2
3
<session-config>
<session-timeout>5</session-timeout> //当前网站每一个session最大空闲时间为5分钟
</session-config>
-------------本文结束感谢您的阅读-------------