Java Web学习笔记

学习来源:尚硅谷

学习时间:2021年9月8日,2023年6月12日

1 XML

1.1 概述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="osg.AndroidExample"
android:installLocation="preferExternal"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8"></uses-sdk>
<uses-feature android:glEsVersion="0x00020000"/> <!-- OpenGL min requierements (2.0) -->
<uses-permission android:name="android.permission.INTERNET"/>

<application android:label="@string/app_name" android:icon="@drawable/osg">
<activity android:name=".osgViewer"
android:label="@string/app_name" android:screenOrientation="landscape"> <!-- Force screen to landscape -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
</manifest>

XML数据格式最主要的功能就是数据传输

主要用途

  • 程序之间的数据传输通讯
  • 配置文件:config.xml
  • 存储数据,充当小型数据库:data.xml
  • 规范数据格式,使得数据具有结构性,易读易处理

1.2 什么是xml

  • xml(Extensible Markup Language)指的是,可扩展性标记语言

  • xml被发明的目的是传输和存储数据,而不是展示数据。

  • xml的标签必须自定义,但是在写标签时一定要有含义。

  • xml是W3C(万维网联盟)推荐的数据传输格式。

1
2
3
4
5
<!-- 必须要有一个根标签 -->
<root>
<user>曾弘毅</user>
<msg>你好,世界</msg>
</root>

xml和html的不同

  1. html的标签不能自定义,xml的标签必须自定义
  2. html语法要求不严格,xml则非常严格
  3. xml用来传输和存储数据,html 用来展示数据

1.3 xml基本语法

1.3.1 语法规则

  1. xml文档必须有根节点。根节点就是其他所有节点的父节点
  2. xml的头声明可有可无,但是建议写:包括版本号和文件编码格式
1
<?xml version="1.0" encoding="utf-8" ?>
  1. xml标签必须成对,标签名大小写敏感,标签不允许交叉编写
  2. 注释形式:
1
<!-- 这是注释 -->
  1. 特殊字符要使用实体名称进行转义
1
2
<msg>if a &lt; b</msg>
<!-- &lt;是<的实体名称 -->

1.3.2 属性规则

1
2
3
4
5
6
7
8
9
<root>
<man>
<name>张三</name>
<age>18</age>
</man>
<man>
<name age="20">李四</name>
</man>
</root>
  • 一个标签可以有多个属性,属性的值必须使用引号
  • xml中属性不太重要,解析属性时,属性会带来额外的解析代码

1.4 xml解析

1.4.1 解析技术介绍

早期的解析技术

早期 JDK 为我们提供了两种 xml 解析技术 DOM 和 Sax (已经过时)。dom 解析技术是 W3C 组织制定的,而所有的编程语言都对这个解析技术使用了自己语言的特点进行实现。 Java 对 dom 技术解析标记也做了实现。 sun 公司在 JDK5 版本对 dom 解析技术进行升级:SAX( Simple API for XML ) SAX 解析,它跟 W3C 制定的解析不太一样。它是以类似事件机制通过回调告诉用户当前正在解析的内容。 它是一行一行的读取 xml 文件进行解析的。不会创建大量的 dom 对象。 所以它在解析 xml 的时候,在内存的使用上。和性能上。都优于 Dom 解析。

第三方解析技术

  • jdom 在 dom 基础上进行了封装 、
  • dom4j 又对 jdom 进行了封装。
  • pull 主要用在 Android 手机开发,是在跟 sax 非常类似都是事件机制解析 xml 文件。

1.4.2 使用dom4j读取xml文档

编程步骤

第一步: 先加载 xml 文件创建 Document 对象

第二步:通过 Document 对象拿到根元素对象

第三步:通过根元素.elelemts(标签名); 可以返回一个集合,这个集合里放着。所有你指定的标签名的元素对象

第四步:找到你想要修改、删除的子元素,进行相应在的操作

第五步,保存到硬盘上

待读取的xml文档

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8" ?>
<books>
<book sn="SN123456">
<name>罗翔说刑法</name>
<price>9.8</price>
<author>张三</author>
</book>
<book sn="SN123457">
<name>老人与海</name>
<price>1.1</price>
<author>海明威</author>
</book>
</books>

获取Document对象

  1. 创建Book类
1
2
3
4
5
public class Book {
private String sn;
private String name;
private BigDecimal price;
}
  1. 获取Document对象
1
2
3
4
5
6
7
8
9
10
11
12
/**
* dom4j获取Document对象
*/
@Test
public void test1() throws DocumentException {
// 要创建一个document对象,我们需要创建一个SAXReader对象
SAXReader saxReader = new SAXReader();
// 这个对象用于读取xml,然后返回一个document对象
Document document = saxReader.read("src/main/resources/book.xml");
// 打印该对象
System.out.println(document);
}

遍历 标签 获取所有标签中的内容

  1. 通过创建 SAXReader 对象。来读取 xml 文件,获取 Document 对象
  2. 通过 Document 对象。拿到 XML 的根元素对象
  3. 通过根元素对象。获取所有的 book 标签对象
  4. 遍历每个 book 标签对象。然后获取到 book 标签对象内的每一个元素,再通过 getText() 方法拿到起始标签和结 束标签之间的文本内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 读取xml中的内容
*/
@Test
public void test2() throws DocumentException {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read("src/main/resources/book.xml");
// 获取根元素对象
Element root = document.getRootElement();
System.out.println(root.asXML());// asXML()将当前元素转化为String对象
// 通过根元素对象,获取所有的book标签对象
List<Element> books = root.elements("book");
// 遍历每个book标签对象,然后获得每个book标签对象的每一个元素
for(Element book : books){
Element name = book.element("name");
Element price = book.element("price");
Element author = book.element("author");
// 通过 getText() 方法拿到起始标签和结束标签之间的文本内容
System.out.println("书名" + name.getText() + " , 价格:" + price.getText() + ", 作者:" + author.getText());
}
}

image-20210911105647311

2 Tomcat

2.1 概述

javaweb的概念

JavaWeb 是指,所有通过 Java 语言编写可以通过浏览器访问的程序的总称,叫 JavaWeb。

JavaWeb 是基于请求和响应来开发的。

请求和响应

  • 请求是指客户端给服务器发送数据,叫请求 Request。

  • 响应是指服务器给客户端回传数据,叫响应 Response。

web资源的分类

web 资源按实现的技术和呈现的效果的不同,又分为静态资源和动态资源两种。

  • 静态资源: html、css、js、txt、mp4 视频 , jpg 图片
  • 动态资源: jsp 页面、Servlet 程序

常用的web服务器

  1. Tomcat:由 Apache 组织提供的一种 Web 服务器,提供对 jsp 和 Servlet 的支持。它是一种轻量级的 javaWeb 容器(服务 器),也是当前应用最广的 JavaWeb 服务器(免费)。
  2. Jboss:是一个遵从 JavaEE 规范的、开放源代码的、纯 Java 的 EJB 服务器,它支持所有的 JavaEE 规范(免费)。
  3. GlassFish: 由 Oracle 公司开发的一款 JavaWeb 服务器,是一款强健的商业服务器,达到产品级质量(应用很少)。
  4. Resin:是 CAUCHO 公司的产品,是一个非常流行的服务器,对 servlet 和 JSP 提供了良好的支持, 性能也比较优良,resin 自身采用 JAVA 语言开发(收费,应用比较多)。
  5. WebLogic:是 Oracle 公司的产品,是目前应用最广泛的 Web 服务器,支持 JavaEE 规范, 而且不断的完善以适应新的开发要求,适合大型项目(收费,用的不多,适合大公司)。

2.2 Tomcat使用

tomcat目录介绍

image-20210912145515541

  • bin:可执行程序
  • conf:配置文件
  • lib:jar包
  • logs:存放服务器运行时输出的日志
  • temp:存放产生的临时数据
  • webapps:存放部署的Web工程,该目录下的每一个目录都是一个web工程项目
  • work:tomcat工作时的目录,用来存放tomcat运行时jsp翻译为servlet的源码,和session钝化的目录

启动tomcat

  1. 方法一:找到bin下的startup.bat文件,双击打开:

image-20210912150804106

测试:localhost:8080

image-20210912150838506

  1. 方法二:采用命令行启动,cd到bin目录下,输入命令
1
catalina run

image-20210912151135972

停止tomcat

  • 双击bin目录下的shutdown.bat
  • 在命令行窗口中按ctrl+C关闭

修改tomcat默认端口号

  • 默认端口号为8080

  • 修改conf/server.xml中Connector标签的port属性

1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

2.3 web工程部署在Tomcat

  1. 方法1:直接将工程目录放置在webapps下
  2. 方法2:在conf/Catalina/localhost下创建一个xml配置文件abc.xml:
1
2
<!-- Context 表示一个工程上下文 path 表示工程的访问路径:/abc docBase 表示你的工程目录在哪里 --> 
<Context path="/abc" docBase="E:\book" />

访问路径:http://ip:port/abc/ 就表示访问 E:\book 目录

手托 html 页面到浏览器和在浏览器中输入 http://ip:端口号/工程名/访问的区别

协议不同,一个是file协议,一个是http协议

image-20210912152841086

image-20210912152948269

ROOT工程访问

  • 当我们在浏览器地址栏中输入访问地址如下: http://ip:port/ 没有工程名的时候,默认访问的是webapps下的ROOT工程,即tomcat默认主页

image-20210912153234071

  • 当我们在浏览器地址栏中输入的访问地址如下: http://ip:port/工程名/ ,而没有资源名,默认访问 index.html 页面

2.4 idea整合Tomcat

整合tomcat

image-20210912153448918

image-20210912153545488

在idea中创建web动态工程

image-20210912153839950

web工程目录介绍

image-20210912154228041

  • WEB-INF:是一个受服务器保护的目录,浏览器无法直接访问到该目录的内容
  • web.xml:它是整个动态web工程的配置部署描述文件,可以在这里配置很多web工程的组件,比如Servlet程序,Filter过滤器,Listener监听器,Session超时等等

idea在tomcat部署web工程

  1. 建议修改 web 工程对应的 Tomcat 运行实例名称:

image-20210912155736375

image-20210912155959681

3 Servlet

3.1 Servlet技术

3.1.1 什么是Servlet

  1. Servlet 是 Java EE 规范之一。规范就是接口 。

  2. Servlet 就 Java Web 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器

  3. Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端。

3.1.2 手动实现Servlet程序

  1. 编写一个类去实现 Servlet 接口

  2. 实现 service 方法,处理请求,并响应数据

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
public class HelloServlet implements Servlet {

@Override
public void init(ServletConfig servletConfig) throws ServletException {

}

@Override
public ServletConfig getServletConfig() {
return null;
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("HelloServlet 被访问了");
}

@Override
public String getServletInfo() {
return null;
}

@Override
public void destroy() {

}
}

ServletRequest由Servlet容器来管理,当客户请求到来时,容器创建一个ServletRequest对象,封装请求数据,同时创建一个ServletResponse对象,封装响应数据。这两个对象将被容器作为service方法的参数传递给Servlet,Servlet利用ServletRequest对象获取客户端发来的请求数据,利用ServletResponse对象发送响应数据。

  1. web.xml 中去配置 servlet 程序的访问地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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标签给tomcat配置servlet程序 -->
<servlet>
<!-- 给servlet程序起别名,一般是类名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet-class是servlet程序的全类名 -->
<servlet-class>com.hongyi.servlet_demo.HelloServlet</servlet-class>
</servlet>

<!-- servlet-mapping标签给servlet程序配置访问地址 -->
<servlet-mapping>
<!-- 告诉tomcat,我当前配置的地址给那个servlet程序使用 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 配置访问地址 -->
<!-- / 在tomcat解析的时候,表示地址为http://ip:port/工程路径 -->
<!-- /hello 表示地址为http://ip:port/工程路径/hello -->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>

image-20210915113256758

注意:tomcat10之后servlet依赖包名不是javax.servlet,而是jakarta.servlet,maven依赖如下:

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>jakarta.servlet.jsp</groupId>
<artifactId>jakarta.servlet.jsp-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency

url地址到Servlet程序的访问

image-20210915114256579

3.1.3 servlet生命周期

  1. 执行 Servlet 构造器方法

  2. 执行 init 初始化方法 :第一、二步,是在第一次访问的时候,创建 Servlet 程序会调用。

  3. 执行 service 方法:第三步,每次访问都会调用

  4. 执行 destroy 销毁方法 :第四步,在 web 工程停止的时候调用。

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
public class HelloServlet implements Servlet {

public HelloServlet() {
System.out.println("1 构造方法被调用");
}

@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2 init方法被调用");
}

@Override
public ServletConfig getServletConfig() {
return null;
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3 HelloServlet 被访问了");
}

@Override
public String getServletInfo() {
return null;
}

@Override
public void destroy() {
System.out.println("4 destroy方法被调用");
}
}

image-20210915114810030

3.1.4 GET 和 POST 请求的分发处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("3 HelloServlet 被访问了");
// 类型转换
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
// 获取请求方式
String method = httpServletRequest.getMethod();
if ("GET".equals(method)){
doGet();
}else if("POST".equals(method)){
doPost();
}
}

private void doPost() {
System.out.println("接收到POST请求");
}

private void doGet() {
System.out.println("接收到GET请求");
}

注:HttpServletRequestServletRequest都是接口,并且HttpServletRequest继承自ServletRequest;HttpServletRequest比ServletRequest多了一些针对于Http协议的方法,如getHeader,getMethod,getSession等等。同理也有HttpServletResponse

3.1.5 通过继承HttpServlet实现Servlet程序

一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序。 而不是实现Servlet接口(见3.2节

  1. 编写一个类去继承 HttpServlet 类

  2. 根据业务需要重写 doGetdoPost 方法

  3. 到 web.xml 中的配置 Servlet 程序的访问地址

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HelloServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
System.out.println("HelloServlet2的doGet方法");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
System.out.println("HelloServlet2的doPost方法");
}
}

HttpServletServlet接口的一个实现类。Servlet框架是由两个Java包组成:javax.servlet和javax.servlet.http。 在javax.servlet包中定义了所有的Servlet类都必须实现或扩展的的通用接口和类,在javax.servlet.http包中定义了采用HTTP通信协议的HttpServlet。

3.1.6 使用idea创建servlet程序

image-20210916144520580

image-20210916144652354

自动生成的servlet代码:

1
2
3
4
5
6
7
8
9
10
11
public class HelloServlet3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}
}

3.1.7 servlet类的继承体系

image-20210916151445030

3.2 ServletConfig类

  • ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。

  • Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。

  • Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象

ServletConfig 类的三大作用

  1. 可以获取 Servlet 程序的别名 servlet-name 的值

  2. 获取初始化参数 init-param

  3. 获取 ServletContext 对象

1
2
3
4
5
6
7
8
9
@Override
public void init(ServletConfig servletConfig) throws ServletException {
// 1. 可以获取 Servlet 程序的别名 servlet-name 的值
System.out.println("该servlet程序的别名是:" + servletConfig.getServletName());
// 2. 获取初始化参数 init-param
System.out.println("初始化参数url的值是:" + servletConfig.getInitParameter("url"));
// 3. 获取 ServletContext 对象
System.out.println(servletConfig.getServletContext());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- servlet标签给tomcat配置servlet程序 -->
<servlet>
<!-- 给servlet程序起别名,一般是类名 -->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet-class是servlet程序的全类名 -->
<servlet-class>com.hongyi.servlet_demo.HelloServlet</servlet-class>
<!-- init-param是初始化参数 -->
<init-param>
<!-- 参数名 -->
<param-name>url</param-name>
<!-- 参数值 -->
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</init-param>
</servlet>

3.3 ServletContext类

3.3.1 什么是ServletContext

  1. ServletContext 是一个接口,它表示 Servlet 上下文对象

  2. 一个 web 工程,只有一个 ServletContext 对象实例

  3. ServletContext 对象是一个域对象。

  4. ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。

什么是域对象?

域对象,是可以像 Map 一样存取数据的对象,叫域对象。

这里的域指的是存取数据的操作范围,而范围则是整个 web 工程。

存数据 取数据 删除数据
Map put get remove
域对象 setAttribute getAttribute removeAttribute

3.3.2 ServletContext 类的四个作用

  1. 获取 web.xml 中配置的上下文参数 context-param

  2. 获取当前的工程路径,格式: /工程路径

  3. 获取工程部署后在服务器硬盘上的绝对路径

  4. 像 Map 一样存取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ContextServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取 web.xml 中配置的上下文参数 context-param
ServletContext context = getServletConfig().getServletContext();
String username = context.getInitParameter("username");
String password = context.getInitParameter("password");
System.out.println(username + password);
// 2. 获取当前的工程路径,格式: /工程路径
System.out.println(context.getContextPath()); // /servlet_demo
// 3. 获取工程部署后在服务器硬盘上的绝对路径
// 斜杠/ 映射到idea代码的web目录
System.out.println("工程部署的路径是:" + context.getRealPath("/"));
System.out.println("工程下css目录的绝对路径是:" + context.getRealPath("/css"));
}
}
1
2
3
4
5
6
7
8
9
<!-- context-param是上下文参数(它属于整个web工程) -->
<context-param>
<param-name>username</param-name>
<param-value>hongyi</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>root</param-value>
</context-param>

image-20210916155320045

1
2
3
4
// 4. 像 Map 一样存取数据
ServletContext context1 = getServletContext();
context1.setAttribute("k1", "v1");
System.out.println(context1.getAttribute("k1"));

3.4 Http协议

什么是协议?

协议是指双方,或多方,相互约定好,大家都需要遵守的规则,叫协议。

所谓 HTTP 协议,就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议。 HTTP 协议中的数据又叫报文。

3.4.1 请求的 HTTP 协议格式

GET请求

  1. 请求行:

(1)请求的方式:GET

(2)请求的资源路径[+?+请求参数] 后面的是可选参数

(3)请求的协议的版本号:HTTP/1.1

  1. 请求头:

key:value 组成 不同的键值对,表示不同的含义

image-20210919141228036

POST请求

  1. 请求行:

(1)请求的方式:POST

(2)请求的资源路径[+?+请求参数] 后面的是可选参数

(3)请求的协议的版本号:HTTP/1.1

  1. 请求头:

key:value 组成 不同的键值对,表示不同的含义

空行

  1. 请求体:发送给服务器的数据

image-20210919141732677

哪些是GET请求,哪些是POST请求

  • GET 请求有哪些:
  1. form 标签 method=get

  2. a 标签

  3. link 标签引入 css

  4. Script 标签引入 js 文件

  5. img 标签引入图片

  6. iframe 引入 html 页面

  7. 在浏览器地址栏中输入地址后敲回车

  • POST 请求有哪些:
  1. form 标签 method=post

3.4.2 响应的 HTTP 协议格式

  1. 响应行:

(1)响应的协议和版本号

(2)响应状态码

(3)响应状态描述符

  1. 响应头:kv键值对

空行

  1. 响应体:回传给客户端的数据

image-20210919142200965

3.4.3 常见的响应码

  • 2xx :表示【成功】处理了请求的状态代码
    • 200:表示请求成功
  • 3xx :表示要完成请求,需要进一步操作。通常,这些状态代码用来【重定向】
    • 表示请求重定向
  • 4xx :表示【请求可能出错】,妨碍了服务器的处理
    • 400:Bad Request,错误请求,表示客户端请求的语法错误,服务器无法理解,例如 url 含有非法字符、json 格式有问题。
    • 401:Unauthorized,请求要求身份验证。对于需要登录的网页,服务器可能返回此响应。
    • 403:Forbidden,表示服务器理解请求客户端的请求,但是拒绝请求。
    • 404:Not Found,服务器无法根据客户端的请求找到资源(网页)。
    • 405:Method Not Allowed,禁用请求中指定的方法。
  • 5xx :表示【服务器】在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错
    • 500:Internal Server Error,服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
    • 501:Not Implemented,服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
    • 502:Bad Gateway,服务器作为网关或代理,从上游服务器收到无效响应。
    • 503:Service Unavailable,服务器作为网关或代理,从上游服务器收到无效响应。
    • 504:Gateway Timeout,服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。

3.4.4 http1.0和2.0的区别

http1.0 http2.0
连接方式 无状态的,每次请求都需要建立新的连接,这意味着每次请求都需要进行TCP握手,这会导致大量延迟。 支持多路复用,可以在一个tcp连接上并发多个请求或响应。
数据格式 数据是文本格式,方便阅读但不利于传输和解析。 数据是二进制格式,更利于传输和解析。
头部压缩 请求头部信息是明文,会导致大量的冗余数据。 请求头部是经过了压缩的,减少了数据传输量,并且使用了HPACK去除了冗余的头部字段。
服务器推送 客户端需要明确请求服务器才能获取资源。 服务器可以主动向客户端推送资源。服务端预测到客户端需要的资源后,在客户端请求之前就将这些资源发送到客户端存储到缓存中,

关于多路复用和服务器推送的场景示例:

在这里插入图片描述

假设有一个网页,它的 HTML 文件中包含了对一个 CSS 文件和一个 JavaScript 文件的引用。

当用户访问这个网页时,服务器首先会返回 HTML 文件。

在 HTTP/1.x 中,浏览器需要解析 HTML 文件,发现 CSS 文件和 JavaScript 文件的引用后,再向服务器发送请求获取这两个文件。

而在 HTTP/2 中,服务器在发送 HTML 文件的同时,可以预测到浏览器接下来会请求 CSS 文件和 JavaScript 文件,

因此,服务器可以在发送 HTML 文件的同时,将 CSS 文件和 JavaScript 文件推送到浏览器的缓存中。

这样,当浏览器解析 HTML 文件,发现需要 CSS 文件和 JavaScript 文件时,就可以直接从缓存中获取,而不需要再向服务器发送请求。

3.4.5 http和https的区别

HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全。使用TCP端口为:80。

Https为安全的超文本传输协议,网景公式设计了SSL(Secure Sockets Layer)协议用于对Http协议传输的数据进行加密,保证会话过程中的安全性。使用TCP端口默认为443。

3.4.6 http无状态和无连接的理解

① 无状态

无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送数据过来,但是,发送完后不会记录任何信息。

HTTP 协议这种特性有优点也有缺点,优点在于解放了服务器,每一次请求“点到为止”不会造成不必要连接占用,缺点在于每次请求会传输大量重复的内容信息。

客户端与服务器进行动态交互的 Web 应用程序出现之后,HTTP 无状态的特性严重阻碍了这些应用程序的实现,毕竟交互是需要承前启后的,简单的购物车程序也要知道用户到底在之前选择了什么商品。于是,两种用于保持 HTTP 连接状态的技术就应运而生了:

  • Cookie:详见[[Java Web学习笔记#4 Cookie]]
  • Session:详见[[Java Web学习笔记#5 Session]]
② 无连接

首先提出问题:怎么理解TCP是面向连接的,HTTP基于TCP却是无连接的?

无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

随着时间的推移,网页变得越来越复杂,里面可能嵌入了很多图片,这时候每次访问图片都需要建立一次 TCP 连接就显得很低效。后来,Keep-Alive 被提出用来解决这效率低的问题。

Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了重新建立连接。从HTTP/1.1起,默认都开启了Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。

Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。 虽然这里使用TCP连接保持了一段时间,但是这个时间是有限范围的,到了时间点依然是会关闭的,所以我们还把其看做是每次连接完成后就会关闭。于提供静态内容的网站来说,这个功能通常很有用。但是,对于负担较重的网站来说,比如当Web服务器和应用服务器在同一台机器上运行时,Keep-Alive 功能对资源利用的影响尤其突出。

3.5 HttpServletRequest类

3.5.1 HttpServlet的请求和响应流程

每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的信息。 HttpServletRequest 和 HttpServletResponse 都是域对象。

  • Web客户向Servlet容器发出Http请求
  • Servlet容器解析Web客户的Http请求
  • Servlet容器创建一个HttpServletRequest对象,在这个对象中封装Http请求信息
  • Servlet容器创建一个HttpServletResponse对象
  • Servlet容器调用HttpServlet的service方法,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象;service里根据请求方法method调用了doGet和doPost等方法。这些方法可以被重写(通过继承HttpServlet
  • HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息
  • HttpServlet调用HttpResponse的有关方法,生成响应数据
  • Servlet容器把HttpServlet的响应结果传给Web客户

3.5.2 常用方法

  1. getRequestURI() :获取请求的资源路径

  2. getRequestURL() :获取请求的统一资源定位符(绝对路径)

  3. getRemoteHost() :获取客户端的 ip 地址

  4. getHeader() :获取请求头

  5. getParameter() :获取请求的参数

  6. getParameterValues() :获取请求的参数(多个值的时候使用)

  7. getMethod():获取请求的方式 GET 或 POST

  8. setAttribute(key, value):设置域数据

  9. getAttribute(key): 获取域数据

  10. getRequestDispatcher() :获取请求转发对象

1
2
3
4
5
6
7
8
9
10
public class RequestAPIServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("URI:" + req.getRequestURI());
System.out.println("URL:" + req.getRequestURL());
System.out.println("客户端IP地址:" + req.getRemoteUser());
System.out.println("请求头user-agent:" + req.getHeader("User-Agent"));
System.out.println("请求方式:" + req.getMethod());
}
}

image-20210919143757656

获取请求参数:

1
2
3
4
5
6
7
8
9
10
11
12
public class ParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby"); // 数组形式
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.asList(hobby));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/parameterServlet" method="get">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
<input type="checkbox" name="hobby" value="java">Java
<input type="checkbox" name="hobby" value="js">Javascript<br/>
<input type="submit">
</form>
</body>
</html>

执行结果:

image-20210919144903197

image-20210919144845836

3.5.3 请求转发forward

请求转发是指,服务器收到请求后,从一个资源跳转到另一个资源的操作叫请求转发。

image-20210919150228023

  • servlet1代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println("在Servlet1中查看的参数" + username);

// 添加域数据
req.setAttribute("key", "servlet1");

// 请求转发至Servlet2
/**
* 请求转发必须以 / 开头
*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
requestDispatcher.forward(req, resp);
}
}
  • servlet2代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Servlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println("在Servlet1中查看的参数" + username);

// 查看servlet1是否有数据
Object key = req.getAttribute("key");
System.out.println("servlet1的key:" + key);

// 处理自己的业务
System.out.println("servlet2处理自己的业务...");
}
}
  • 请求地址
1
http://localhost:8080/servlet1?username=zym
  • 执行结果

image-20210919151332578

特点

  1. 浏览器地址栏没有变化
  2. 他们是一次请求
  3. 他们共享Request域中的数据
  4. 可以转发到WEB-INF目录下
  5. 不可以访问工程以外的资源

请求转发与请求重定向的区别

^388685

请求转发和请求重定向主要区别,包含以下 5 点: ^035230

  • 定义不同

请求转发(Forward):发生在服务端程序内部,当服务器端收到一个客户端的请求之后,会先将请求,转发给目标地址,再将目标地址返回的结果转发给客户端。是一次请求,客户端无感知。

请求重定向(Redirect):请求重定向指的是服务器端接收到客户端的请求之后,会给客户端返回了一个临时响应头,这个临时响应头中记录了,客户端需要再次发送请求(重定向)的 URL 地址,客户端再收到了地址之后,会将请求发送到新的地址上,这就是请求重定向。是二次请求,客户端有感知。

  • 请求方不同

请求转发是服务器端的行为,服务器端代替客户端发送请求,并将结果返回给客户端;而请求重定向是客户端的行为。

image-20230612210710304

  • 数据共享不同

请求转发是服务器端实现的,所以整个执行流程中,客户端(浏览器端)只需要发送一次请求,因此整个交互过程中使用的都是同一个 Request 请求对象和一个 Response 响应对象,所以整个请求过程中,请求和返回的数据是共享的;

而请求重定向客户端发送两次完全不同的请求,所以两次请求中的数据是不同的。

  • 代码实现不同
1
2
3
request.getRequestDispatcher("/index.html").forward(request, response); // request是HttpServletRequest类对象,请求转发到/index.html

response.sendRedirect("/index.html"); // response是HttpServletResponse类对象,重定向到/index.html

3.6 HttpServletResponse类

3.6.1 作用

HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,

我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置。

3.6.2 使用

需求:往客户端回传 字符串 数据。

1
2
3
4
5
6
7
public class ResponseIOServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.write("Response's Content!");
}
}

3.6.3 请求重定向redirect

请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。你去新地址访问(因为之前的地址可能已经被废弃)。

image-20230612212331246

实现策略:

  • 方案1:
1
2
resp.setStatus(302);
resp.setHeader("Location", "http://localhost:8080");
  • 方案2:
1
resp.sendRedirect("http://localhost:8080");

请求转发和请求重定向区别见3.5.3节[[Java Web学习笔记#^388685]]

4.1 简介

cookie是由服务器发送给客户端(浏览器)的小量信息,客户端使用键值对进行存储。

原理:客户端请求服务器时,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。而客户端浏览器会把Cookie保存起来。当浏览器再请求服务器时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器通过检查该Cookie来获取用户状态。

示例

image-20230613094032269

4.2 使用

4.2.1 创建Cookie

image-20230613093805981

代码示例

  • BaseServlet:重写HttpServlet类的doPost和doGet方法,解决中文乱码,获取请求参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class BaseServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 解决post请求中文乱码问题
// 一定要在获取请求参数之前调用才有效
req.setCharacterEncoding("UTF-8");
// 解决响应中文乱码问题:添加响应头
resp.setContentType("text/html; charset=UTF-8");
String action = req.getParameter("action");
try {
// 获取action业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
// 调用目标业务 方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
  • CookieServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CookieServlet extends BaseServlet {
// ...
protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 创建Cookie对象
Cookie cookie = new Cookie("key4", "value4");
//2 通知客户端保存Cookie
resp.addCookie(cookie);
//1 创建Cookie对象
Cookie cookie1 = new Cookie("key5", "value5");
//2 通知客户端保存Cookie
resp.addCookie(cookie1);
resp.getWriter().write("Cookie创建成功");
}
}
  • web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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>CookieServlet</servlet-name>
<servlet-class>com.hongyi.servlet.CookieServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieServlet</servlet-name>
<url-pattern>/cookieServlet</url-pattern>
</servlet-mapping>
</web-app>
  • cookie.html
1
<a href="cookieServlet?action=createCookie" target="target">Cookie的创建</a>

执行效果:

image-20230613102106263

此时浏览器将cookie信息保存在了客户端。

4.2.2 服务器获取Cookie

image-20230613102148880

例如:

image-20230613102733696

代码示例

  • 工具类CookieUtil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CookieUtils {
/**
* 查找指定名称的Cookie对象
* @param name
* @param cookies
* @return
*/
public static Cookie findCookie(String name , Cookie[] cookies){
if (name == null || cookies == null || cookies.length == 0) {
return null;
}
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie;
}
}
return null;
}
}
  • CookieServlet新增方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected void getCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

Cookie[] cookies = req.getCookies();

for (Cookie cookie : cookies) {
// getName方法返回Cookie的key
// getValue方法返回Cookie的value
resp.getWriter().write("Cookie[" + cookie.getName() + "=" + cookie.getValue() + "] <br/>");
}

Cookie iWantCookie = CookieUtils.findCookie("key1", cookies);

// 如果不等于null,说明赋过值,也就是找到了需要的Cookie
if (iWantCookie != null) {
resp.getWriter().write("找到了需要的Cookie");
}
}
  • 页面
1
<a href="cookieServlet?action=getCookie" target="target">Cookie的获取</a>

执行结果:

image-20230613102535659

4.2.3 Cookie值的修改

方法1:创建一个key相同的cookie进行覆盖:

1
2
3
// 例如存在键值对key1=value1
Cookie cookie = new Cookie("key1","newValue1");
resp.addCookie(cookie);

方法2:

  • 先查找到需要修改的 Cookie 对象
  • 调用 setValue()方法赋于新的 Cookie 值
  • 调用 response.addCookie()通知客户端保存修改
1
2
3
4
5
6
7
8
9
10
11
protected void updateCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.查找
Cookie cookie = CookieUtils.findCookie("key1", req.getCookies());
if (cookie != null) {
// 2.修改
cookie.setValue("newValue1");
// 3.添加
resp.addCookie(cookie);
}
resp.getWriter().write("key1的Cookie已经修改好");
}

4.3 生命周期

cookie有2种存储方式,一种是会话性,一种是持久性。

  • 会话性:如果cookie为会话性,那么cookie仅会保存在客户端的内存中,当我们关闭客服端时cookie也就失效了
  • 持久性:如果cookie为持久性,那么cookie会保存在用户的硬盘中,直至生存期结束或者用户主动将其销毁。

相关方法:

1
cookie.setMaxAge(int expiry); // 设置cookie的有效期,默认为-1。这个如果设置负数,表示客服端关闭,cookie就会删除。0表示马上删除。正数表示有效时间,单位是秒。

代码示例

需求:免用户名登录。

image-20230613104658331

  • 页面
1
2
3
4
5
<form action="http://localhost:8080/loginServlet" method="get">
用户名:<input type="text" name="username" value="${cookie.username.value}"> <br>
密码:<input type="password" name="password"> <br>
<input type="submit" value="登录">
</form>
  • LoginServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LoginServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if ("wzg168".equals(username) && "123456".equals(password)) {
// 登录成功
Cookie cookie = new Cookie("username", username);
cookie.setMaxAge(60 * 60 * 24 * 7); // 当前Cookie一周内有效
resp.addCookie(cookie);
System.out.println("登录 成功");
} else {
// 登录失败
System.out.println("登录 失败");
}
}
}
  • web.xml
1
2
3
4
5
6
7
8
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.hongyi.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/loginServlet</url-pattern>
</servlet-mapping>

5 Session

5.1 简介

Session机制是一种服务器端的机制,服务器使用散列表来保存客户端的信息。

相比于保存在客户端的Cookie,Session将用户交互信息保存在了服务器端,使得同一个客户端每次和服务端交互时,不需要每次都传回所有的Cookie值,而是只要传回一个ID(sessionId),这个ID是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样就实现了一个ID就能在服务器取得所有的用户交互信息。

意义:在无连接(HTTP)协议基础之上实现在用户状态管理。

5.2 作用范围

一般来说,每次请求不同的域都会新创建一个session:

  • 对于多标签的浏览器来说,在一个浏览器窗口中,多个标签同时访问一个页面,session是一个。
  • 对于多个浏览器窗口之间,同时或者相隔很短时间访问一个页面,session是多个的,和浏览器的进程有关。
  • 对于一个同一个浏览器窗口,直接录入url访问同一应用的不同资源,session是一样的。

5.3 使用

5.3.1 创建和获取Session对象

image-20230613114047896

1
2
3
4
5
6
7
8
9
// 第一次调用是:创建 Session 会话
// 之后调用都是:获取前面创建好的 Session 会话对象。
request.getSession();

// 判断是不是刚创建出来的会话
session.isNew();

// 得到 Session 的会话 id 值
session.getId();

代码示例

  • 页面
1
<a href="sessionServlet?action=createOrGetSession" target="target">Session的创建和获取(id号、是否为新创建)</a>
  • SessionServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SessionServlet extends BaseServlet {
protected void createOrGetSession(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建和获取Session会话对象
HttpSession session = req.getSession();
// 判断当前Session会话,是否是新创建出来的
boolean isNew = session.isNew();
// 获取Session会话的唯一标识 id
String id = session.getId();

resp.getWriter().write("得到的Session,它的id是:" + id + " <br /> ");
resp.getWriter().write("这个Session是否是新创建的:" + isNew + " <br /> ");
}
}

打印结果:

1
2
得到的Session,它的id是:0F2915A82E9B66089DF0817E15F12EA7
这个Session是否是新创建的:false

5.3.2 Session域数据的存取

session是一个域对象,与之id相配时就能共享它的数据。范围比request大。只要能取到对应的id即便重新发起请求也能共享数据。

域对象通用方法如下:

方法 返回值 描述
setAttribute(String key,Object value) void 将数据以键值对的形式保存在对象中
getAttribute(String key) Objcet 根据存入的key 来获取对应的值
removeAttribute(String key) void 根据key来删除对应的值

代码示例

1
2
3
4
5
6
7
8
9
protected void setAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().setAttribute("key1", "value1");
resp.getWriter().write("已经往Session中保存了数据");
}

protected void getAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object attribute = req.getSession().getAttribute("key1");
resp.getWriter().write("从Session中获取出key1的数据是:" + attribute);
}

5.4 生命周期

创建:当服务器第一次执行request.getSession()或者执行这句话没有与之相匹配的id时创建一个Session;

销毁:session默认30分钟不使用时销毁。注意是不使用,也就是你在30分钟内不挂机的话它就不会自动销毁。

销毁时间设置:

1
2
// 设置 Session 的超时时间(以秒为单位),超过指定的时长,Session就会被销毁。值为正数的时候,设定 Session 的超时时长。负数表示永不超时(极少使用)。
session.setMaxInactiveInterval(int interval);

或者在web.xml全局配置:

1
2
3
4
<!--表示当前web工程。创建出来 的所有Session默认是20分钟 超时时长-->
<session-config>
<session-timeout>20</session-timeout>
</session-config>

或者马上超时:

1
2
// 让 Session 会话马上超时
session.invalidate();

5.5 和Cookie的关系

Session 技术,底层其实是基于 Cookie 技术来实现的。

image-20230613111602863

区别

image-20230613112557066

6 Filter

6.1 简介

Filter 过滤器它是 JavaWeb 的三大组件之一。

作用:拦截请求,过滤响应。Servlet 标准中定义了过滤器,可以拦截任意的服务器请求和响应。

6.2 过滤器实现

需求:在 web 工程下,有一个 admin 目录。这个 admin 目录下的所有资源(html 页面、jpg 图片、jsp 文件、等等)都必须是用户登录之后才允许访问。

使用过滤器:

image-20230613114409707

代码实现

  • filter.AdminFilter
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
// 实现Filter接口,重写方法
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}

/**
* doFilter方法,专门用于拦截请求。可以做权限检查
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
// 获取session对象
HttpSession session = httpServletRequest.getSession();
Object user = session.getAttribute("user");
// 没有登录
if (user == null) {
// 请求重定向
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
} else {
// 让程序继续往下访问用户的目标资源
// 如果不执行这个方法,表示对所有后续逻辑的拦截
filterChain.doFilter(servletRequest, servletResponse);
}
}

@Override
public void destroy() {
Filter.super.destroy();
}
}
  • web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 用于配置一个过滤器 -->
<filter>
<!-- 过滤器名字,随意 -->
<filter-name>AdminFilter</filter-name>
<!-- 过滤器类的全类名 -->
<filter-class>com.hongyi.filter.AdminFilter</filter-class>
</filter>

<!-- 配置过滤器的拦截路径 -->
<filter-mapping>
<!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
<filter-name>AdminFilter</filter-name>
<!-- 拦截路径
/ 表示请求地址为:http://ip:port/工程路径/ ,映射到 IDEA 的 web 目录
/admin/* 表示请求地址为:http://ip:port/工程路径/admin/*
-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>

步骤:

  1. 编写一个类去实现 Filter 接口
  2. 实现过滤方法 doFilter()
  3. 到 web.xml 中去配置 Filter 的拦截路径

6.3 生命周期

  1. 在Web容器启动时候创建Filter对象,Filter对象是单例的(只创建一个Filter对象)
  2. 创建以后,立即执行init()方法,只执行一次
  3. 在有url请求时候,会执行匹配的doFilter()doFilter()是并发执行的。
  4. Filter对象在容器关闭时候销毁,销毁时候执行destroy()

6.4 过滤器链

image-20230613115907206

6.5 拦截路径

  • 精确匹配
1
<url-pattern>/target.jsp</url-pattern>

以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp

  • 目录匹配
1
<url-pattern>/admin/*</url-pattern>
  • 后缀名匹配
1
<url-pattern>*.html</url-pattern>

7 Listener

7.1 简介

监听器:专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生特定情况时,立即采取相应的行动。

Servlet监听器:Servlet规范中定义的一种特殊类,它用于监听Web应用程序中的ServletContext,HttpSession 和HttpServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。

特定情况包括:

  • 对象创建:
    • request对象,response对象,session对象,ServletContext对象等
    • 对象的创建和销毁时候执行事件代码
  • 在对象中添加、删除数据时候(setAttribute,removeAttribute)

监听器分类:

image-20230613140920133

  • 域对象监听器
  • 域对象的属性域监听器
  • Session域中数据的监听器

7.2 相关类

7.2.1 ServletContextListener

作用:监听ServletContext对象的创建与销毁。

方法名 作用
contextInitialized(ServletContextEvent sce) ServletContext创建时调用
contextDestroyed(ServletContextEvent sce) ServletContext销毁时调用

ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。

7.2.2 HttpSessionListener

作用:监听HttpSession对象的创建与销毁。

方法名 作用
sessionCreated(HttpSessionEvent hse) HttpSession对象创建时调用
sessionDestroyed(HttpSessionEvent hse) HttpSession对象销毁时调用

7.2.3 ServletRequestListener

作用:监听ServletRequest对象的创建与销毁

方法名 作用
requestInitialized(ServletRequestEvent sre) ServletRequest对象创建时调用
requestDestroyed(ServletRequestEvent sre) ServletRequest对象销毁时调用

7.2.4 ServletContextAttributeListener

作用:监听ServletContext中属性的创建、修改和销毁

方法名 作用
attributeAdded(ServletContextAttributeEvent scab) 向ServletContext中添加属性时调用
attributeRemoved(ServletContextAttributeEvent scab) 从ServletContext中移除属性时调用
attributeReplaced(ServletContextAttributeEvent scab) 当ServletContext中的属性被修改时调用

7.2.5 HttpSessionAttributeListener

7.2.6 ServletRequestAttributeListener

7.3 使用

示例1

学习SpringMVC的时候,会用到一个ContextLoaderListener,这个监听器就实现了ServletContextListener接口,表示对ServletContext对象本身的生命周期进行监控。

  • 创建类实现ServletContextListener接口,并重写方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ListenerTest implements ServletContextListener {
/**
*
* @param sce ServletContextEvent对象代表本次事件,通过这个对象可以获取ServletContext对象本身
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext);
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContextListener.super.contextDestroyed(sce);
}
}
  • 注册监听器
1
2
3
<listener>
<listener-class>com.hongyi.listener.ListenerTest</listener-class>
</listener>

示例2

1
2
3
4
5
6
7
8
9
10
11
12
public class DemoListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
System.out.println("SESSION ID: " + session.getId());
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("Hello Session Destroyed");
}
}
1
2
3
<listener>
<listener-class>com.hongyi.listener.DemoListener</listener-class>
</listener>