Tomcat+Servlet

Tomcat+Servlet

XML

1681452257379

XML是EXtensible Markup Language的缩写,翻译过来就是可扩展标记语言。所以很明显,XML和HTML一样都是标记语言,也就是说它们的基本语法都是标签。

  • 可扩展:三个字表面上的意思是XML允许自定义格式。但这不代表你可以随便写;

  • 在XML基本语法规范的基础上,你使用的那些第三方应用程序、框架会通过XML约束的方式强制规定配置文件中可以写什么和怎么写;

  • XML基本语法这个知识点的定位是:**我们不需要从零开始,从头到尾的一行一行编写XML文档,而是在第三方应用程序、框架已提供的配置文件的基础上修改。**要改成什么样取决于你的需求,而怎么改取决XML基本语法和具体的XML约束;

常见配置文件类型

  1. properties: 例如druid连接池就是使用properties文件作为配置文件;
  2. XML,: 例如Tomcat就是使用XML文件作为配置文件;
  3. YAML/YML: 例如SpringBoot就是使用YAML作为配置文件;
  4. json: 通常用来做文件传输,也可以用来做前端或者移动端的配置文件;
  5. ... ...

properties配置文件

示例:

atguigu.jdbc.url=jdbc:mysql://localhost:3306/atguigu
atguigu.jdbc.driver=com.mysql.cj.jdbc.Driver
atguigu.jdbc.username=root
atguigu.jdbc.password=root

# 这种需要通过DruidDataSource的属性获取配置文件的值
DruidDataSource druidDataSource = new DruidDataSource();
//2.添加配置参数
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///atguigu");
druidDataSource.setUsername("root");
druidDataSource.setPassword("12345");
//初始链接的数量
druidDataSource.setInitialSize(5);
//最多有10个连接
druidDataSource.setMaxActive(10);
//最大等待时间  单位毫秒
druidDataSource.setMaxWait(5000);

语法规范:

  • 由键值对组成;
  • 键和值之间的符号是等号;
  • 每一行都必须顶格写,前面不能有空格之类的其他符号;

xml配置文件

示例:

<?xml version="1.0" encoding="UTF-8"?>
<students>
    <student>
        <name>张三</name>
        <age>18</age>
    </student>
    <student>
        <name>李四</name>
        <age>20</age>
    </student>
</students>

XML的基本语法:

  • XML的基本语法和HTML的基本语法简直如出一辙。XML基本语法+HTML约束=HTML语法。在逻辑上HTML确实是XML的子集:

    1、XML文档声明: 这部分基本上就是固定格式,<?xml version="1.0" encoding="UTF-8"?>

    2、根标签:根标签有且只能有一个;

    3、标签关闭:开始标签和结束标签必须成对出现,单标签在标签内关闭;

    4、标签嵌套:标签可以嵌套,但是不能交叉嵌套,注释不能嵌套;

    5、标签名、属性名建议使用小写字母;

    6、属性:属性必须有值,属性值必须加引号,单双都行;

XML的约束(稍微了解):将来主要就是根据XML约束中的规定来编写XML配置文件,在编写XML的时候,编辑工具会根据约束提示。XML约束主要包括DTD和Schema两种。以下是一个web.xml约束的示例:

本质在约束后期再此xml当中能写什么标签

<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">

DOM4J进行XML解析

DOM4J的使用步骤

  1. 导入jar包 dom4j.jar
  2. 创建解析器对象(SAXReader)
  3. 解析xml 获得Document对象
  4. 获取根节点RootElement
  5. 获取根节点下的子节点

DOM4J的API介绍

1、创建SAXReader对象。

SAXReader saxReader = new SAXReader();

2. 解析XML获取Document对象,需要传入要解析的XML文件的字节输入流。

Document document = reader.read(inputStream);

3. 获取文档的根标签。

Element rootElement = documen.getRootElement();

4. 获取标签的子标签。

//获取所有子标签
List<Element> sonElementList = rootElement.elements();
//获取指定标签名的子标签
List<Element> sonElementList = rootElement.elements("标签名");

5. 获取标签体内的文本。

String text = element.getText();

6. 获取标签的某个属性的值。

String value = element.attributeValue("属性名");

例:

  //1、创建SAXreader解析器
  SAXReader saxReader = new SAXReader();
  //2、获取xml文件的io流 stream
  InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("test.xml");
   //	3、通过SAXreader解析器去解析id流  saxReader.read(stream) 得到document
   Document read = saxReader.read(resourceAsStream);
   //	4、通过document.rootELement获取到根标签
   Element rootElement = read.getRootElement();
   //	5、通过根标签rootElement.elements()获取子元素 返回list
   List<Element> elements = rootElement.elements();
   //	6、通过list.forEach()遍历每个子元素获取元素的内容getText() 属性值等
   elements.forEach(o->{
       System.out.println(o.getName());
       List<Element> student = o.elements();
         student.forEach(s->{
         System.out.println(s.getName()+" "+s.getText());
   });

});

Tomcat10

WEB服务器

Web服务器通常由硬件和软件共同构成。

  • 硬件:电脑,提供服务供其它客户电脑访问;
  • 软件:电脑上安装的服务器软件,安装后能提供服务给网络中的其他计算机,将本地文件映射成一个url地址供网络中的其他人访问;
1681441674967

常见的JavaWeb服务器:

  • Tomcat(Apache):当前应用最广的JavaWeb服务器。
  • Jetty:更轻量级、更灵活的servlet容器。
  • JBoss(Redhat红帽):支持JavaEE,应用比较广EJB容器。
  • GlassFish(Orcale):Oracle开发JavaWeb服务器。
  • Resin(Caucho):支持JavaEE,应用越来越广。
  • Weblogic(Orcale):支持JavaEE,适合大型项目。
  • Websphere(IBM):支持JavaEE,适合大型项目。

Tomcat服务器

简介

1681452377469

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器

安装

https://tomcat.apache.org/

版本:

  • 版本:企业用的比较广泛的是8.0和9.0,目前比较新正式发布版本是Tomcat10.0,Tomcat11仍然处于测试阶段;
  • JAVAEE版本和Servlet版本号对应关系 https://jakarta.ee/release/;
  • 版本不一样,后期导包位置名字不一样 4及以下和5及以上的包的名字有区别
Servlet Version EE Version
6.1 Jakarta EE ?
6.0 Jakarta EE 10
5.0 Jakarta EE 9/9.1
4.0 JAVA EE 8
3.1 JAVA EE 7
3.1 JAVA EE 7
3.0 JAVAEE 6
  • Tomcat 版本和Servlet版本之间的对应关系:
Servlet Version **Tomcat ** Version JDK Version
6.1 11.0.x 17 and later
6.0 10.1.x 11 and later
5.0 10.0.x (superseded) 8 and later
4.0 9.0.x 8 and later
3.1 8.5.x 7 and later
3.1 8.0.x (superseded) 7 and later
3.0 7.0.x (archived) 6 and later (7 and later for WebSocket)

下载:

  • Tomcat官方网站:http://tomcat.apache.org/

  • 安装版:需要安装,一般不考虑使用;

  • 解压版:直接解压缩使用,我们使用的版本;

    1681442513261

    安装:

    1. 正确安装JDK并配置JAVA_HOME。
    1681442778503
    1. 解压Tomcat到非中文无空格目录。

    1681442849940

    1. 点击bin/startup.bat启动。

    1681442946756

    1. 打开浏览器输入 http://localhost:8080访问测试。
    1681443038911
    1. 直接关闭窗口或者运行 bin/shutdown.bat关闭tomcat。

    2. 处理dos窗口日志中文乱码问题:修改conf/logging.properties,ConsoleHandler.encoding的UTF-8修改为GBK。

    1681443124315 1681443202115

Tomcat目录及测试

Tomcat的bin目录,conf目录等,我们称之为Tomcat的安装目录或根目录

  • bin:该目录下存放的是二进制可执行文件,如果是安装版,那么这个目录下会有两个exe文件:tomcat10.exe、tomcat10w.exe,前者是在控制台下启动Tomcat,后者是弹出GUI窗口启动Tomcat;如果是解压版,那么会有startup.bat和shutdown.bat文件,startup.bat用来启动Tomcat,但需要先配置JAVA_HOME环境变量才能启动,shutdown.bat用来停止Tomcat;

  • conf:这是一个非常非常重要的目录,这个目录下有四个最为重要的文件:

    • server.xml:配置整个服务器信息。例如修改端口号。默认HTTP请求的端口号是:8080

    • tomcat-users.xml:存储tomcat用户的文件,这里保存的是tomcat的用户名及密码,以及用户的角色信息。可以按着该文件中的注释信息添加tomcat用户,然后就可以在Tomcat主页中进入Tomcat Manager页面了;

      <tomcat-users xmlns="http://tomcat.apache.org/xml"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
                    version="1.0">	
      	<role rolename="admin-gui"/>
      	<role rolename="admin-script"/>
      	<role rolename="manager-gui"/>
      	<role rolename="manager-script"/>
      	<role rolename="manager-jmx"/>
      	<role rolename="manager-status"/>
      	<user 	username="admin" 
      			password="admin" 
      			roles="admin-gui,admin-script,manager-gui,manager-script,manager-jmx,manager-status"
      	/>
      </tomcat-users>
      

      web.xml:部署描述符文件,这个文件中注册了很多MIME类型,即文档类型。这些MIME类型是客户端与服务器之间说明文档类型的,如用户请求一个html网页,那么服务器还会告诉客户端浏览器响应的文档是text/html类型的,这就是一个MIME类型。客户端浏览器通过这个MIME类型就知道如何处理它了。当然是在浏览器中显示这个html文件了。但如果服务器响应的是一个exe文件,那么浏览器就不可能显示它,而是应该弹出下载窗口才对。MIME就是用来说明文档的内容是什么类型的!

    • context.xml:对所有应用的统一配置,通常我们不会去配置它。

  • lib:Tomcat的类库,里面是一大堆jar文件。如果需要添加Tomcat依赖的jar文件,可以把它放到这个目录中,当然也可以把应用依赖的jar文件放到这个目录中,这个目录中的jar所有项目都可以共享之,但这样你的应用放到其他Tomcat下时就不能再共享这个目录下的jar包了,所以建议只把Tomcat需要的jar包放到这个目录下;

  • logs:这个目录中都是日志文件,记录了Tomcat启动和关闭的信息,如果启动Tomcat时有错误,那么异常也会记录在日志文件中。

  • temp:存放Tomcat的临时文件,这个目录下的东西可以在停止Tomcat后删除!

  • webapps:存放web项目的目录,其中每个文件夹都是一个项目;如果这个目录下已经存在了目录,那么都是tomcat自带的项目。其中ROOT是一个特殊的项目,在地址栏中访问:http://127.0.0.1:8080,没有给出项目目录时,对应的就是ROOT项目.http://localhost:8080/examples,进入示例项目。其中examples"就是项目名,即文件夹的名字。

  • work:运行时生成的文件,最终运行的文件都在这里。通过webapps中的项目生成的!可以把这个目录下的内容删除,再次运行时会生再次生成work目录。当客户端用户访问一个JSP文件时,Tomcat会通过JSP生成Java文件,然后再编译Java文件生成class文件,生成的java和class文件都会存放到这个目录下。

  • LICENSE:许可证。

  • NOTICE:说明文件。

WEB项目的标准结构(webapps)

一个标准的可以用于发布的WEB项目标准结构如下:

image-20240828082609331

  • app 本应用根目录
    • static 非必要目录,约定俗成的名字,一般在此处放静态资源 ( css js img);
    • WEB-INF必要目录,必须叫WEB-INF。受保护的资源目录,浏览器通过url不可以直接访问的目录;
      • classes 必要目录,src下源代码、配置文件,编译后会在该目录下。web项目中如果没有Java源码,则该目录不会出现。
      • lib 必要目录,项目依赖的jar编译后会出现在该目录下,web项目要是没有依赖任何jar,则该目录不会出现。
      • web.xml 必要文件,web项目的基本配置文件.,较新的版本中可以没有该文件,但是学习过程中还是需要该文件。
    • index.html 非必要文件,index.html/index.htm/index.jsp为默认的欢迎页;

url的组成部分和项目中资源的对应关系:

1681456161723

WEB项目部署的方式

方式1 直接将编译好的项目放在webapps目录下 ,然后在浏览器中通过更改路径访问项目。

方式2 将编译好的项目打成war包放在webapps目录,tomcat启动后会自动解压war包(其实和第一种一样,后面通过maven完成)。

方式3 可以将项目放在非webapps的其他目录下,在Tomcat中通过配置文件指向app的实际磁盘路径。

  • 在磁盘的自定义目录上准备一个app
1681456447284
  • 在tomcat的conf下创建Catalina/localhost目录,并在该目录下准备一个app.xml文件
<!-- 
	path: 项目的访问路径,也是项目的上下文路径,就是在浏览器中,输入的项目名称
    docBase: 项目在磁盘中的实际路径
 -->
<Context path="/app" docBase="D:\mywebapps\app" />
  • 启动Tomcat访问测试即可

IDEA中开发并部署运行WEB项目

IDEA关联本地Tomcat

可以在创建项目前设置本地Tomcat,也可以在打开某个项目的状态下找到settings:

找到 Build>Execution>Eeployment下的Application Servers ,找到+号:

选择Tomcat Server:

选择Tomcat的安装目录:

点击ok:

image-20240821151006457

IDEA创建web工程

推荐先创建一个空项目,既workspace,这样可以在一个空项目下同时存在多个modules,不用后续来回切换之前的项目,当然也可以忽略此步直接创建项目:

检查项目的SDK,语法版本,以及项目编译后的输出目录:

先创建一个普通的Java项目:

image-20240821151402592

创建完毕后,为项目添加Tomcat依赖:

image-20240821151536518

添加 web-modules(web目录)

image-20240821152638231

artifacts中添加本web目录

image-20240821152753394

或者--》选择modules,在help中搜索support 添加 framework support:

image-20240824083400485

选择Web Application 注意Version,勾选 Create web.xml:

1681459007273

删除项目步骤

​ 1、先移除项目
​ 右击项目 -- Remove Module(项目变灰) -- 右击删除

​ 2、删除wer包
​ file -- Project Structure -- Project settings -- Artifacts 选择- 移除要删除的wer包

处理配置文件:

  • 在工程下创建resources目录,专门用于存放配置文件(都放在src下也行,单独存放可以尽量避免文件集中存放造成的混乱);
  • 标记目录为资源目录,不标记的话则该目录不参与编译;

1681461443278

  • 标记完成后,显示效果如下:

1681461513406

处理依赖jar包问题:

  • 在WEB-INF下创建lib目录。
  • 必须在WEB-INF下,且目录名必须叫lib!!!
  • 复制jar文件进入lib目录。

1681461788411

  • 将lib目录添加为当前项目的依赖,后续可以用maven统一解决。

1681461846178

1681461881121
  • 环境级别推荐选择module 级别,降低对其他项目的影响,name可以空着不写。

1681461923761

IDEA部署-运行web项目

检查idea是否识别modules为web项目,并且存在将项目构建成发布结构的配置:

  • 就是检查工程目录下,web目录有没有特殊的识别标记。

1681462523901

  • 以及artifacts下,有没有对应 _war_exploded,如果没有,就点击+号添加。

1681462584524

点击向下箭头,出现 Edit Configurations选项:

1681462621908

出现运行配置界面:

1681462710108

点击+号,添加本地Tomcat服务器:

1681462754191

因为IDEA 只关联了一个Tomcat,红色部分就只有一个Tomcat可选:

1681462798933

选择Deployment,通过+添加要部署到Tomcat中的artifact:

1681463011546

Application context中是默认的项目上下文路径,也就是url中需要输入的路径,这里可以自己定义,可以和工程名称不一样,也可以不写,但是要保留/,这里暂时就用默认的:

项目上下文路径:在idea中我们可以自定义项目的访问路径,也叫上下文路径 contextPath

1681463049807

点击apply 应用后,回到Server部分。After Launch是配置启动成功后,是否默认自动打开浏览器并输入URL中的地址,HTTP port是HTTP连接器目前占用的端口号:

1681463212587

点击OK后,启动项目,访问测试;

IDEA部署并运行项目的原理:

  • idea并没有直接进将编译好的项目放入Tomcat的webapps中;
  • idea根据关联的Tomcat,创建了一个Tomcat副本,将项目部署到了这个副本中;
  • idea的Tomcat副本在C:\用户\当前用户\AppData\Local\JetBrains\IntelliJIdea2022.2\tomcat\中;
  • idea的Tomcat副本并不是一个完整的tomcat,副本里只是准备了和当前项目相关的配置文件而已;
  • idea启动Tomcat时,是让本地Tomcat程序按照Tomcat副本里的配置文件运行;
  • idea的Tomcat副本部署项目的模式是通过conf/Catalina/localhost/*.xml配置文件的形式实现项目部署的;

1681521240438

最终idea中web项目源码目录结构

	resources: 存放项目的配置文件的  创建目录需要 mark as 
	src:java的源码
	web:前端的资源文件
		WEB-INF目录  服务器保护的资源文件,只能是程序内部才能访问,不能浏览器直接去请求访问
			lib目录  放web项目的第三方jar包位置
			web.xml  javaweb的核心配置文件

HTTP协议

HTTP简介

1681522638617 1681522600239

HTTP 超文本传输协议 (HTTP-Hyper Text transfer p0l.),是一个基于tcp底层协议属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过十几年的使用与发展,得到不断地完善和扩展。它是一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。客户端与服务端通信时传输的内容我们称之为**报文HTTP协议规定了报文的格式。HTTP就是一个通信规则,这个规则规定了客户端发送给服务器的报文格式,也规定了服务器发送给客户端的报文格式。实际我们要学习的就是这两种报文。客户端发送给服务器的称为"请求报文"服务器发送给客户端的称为"响应报文"**。

HTTP协议的会话方式

浏览器与服务器之间的通信过程要经历四个步骤。

  • 浏览器与WEB服务器的连接过程是短暂的,每次连接只处理一个请求和响应。对每一个页面的访问,浏览器与WEB服务器都要建立一次单独的连接。
  • 浏览器到WEB服务器之间的所有通讯都是完全独立分开的请求和响应对。

HTTP1.0和HTTP1.1的区别

在HTTP1.0版本中,浏览器请求一个带有图片的网页,会由于下载图片而与服务器之间开启一个新的连接;但在HTTP1.1版本中,允许浏览器在拿到当前请求对应的全部资源后再断开连接,提高了效率。

请求和响应报文

报文的格式

报文就是协议的规定: 行 头 空行 体

报文
	行
	头
	体
	
前端发请求给后端 就会携带请求报文 到了tomcat -> tomcat就会把请求报文封装成一个对象,我们就可以在java代码当中使用这个对象获取请求报文的所有内容


​ 响应报文在tomcat里面也会事先封装好这个对象,我们后期java处理完逻辑之后想要给前端返回什么数据,只需要把数据放入tomcat给我们准备好的响应报文对应的对象里面即可

请求报文

客户端发给服务端的报文:

  • 请求报文格式
    • 请求首行(请求行); GET/POST 资源路径?参数 HTTP/1.1
    • 请求头信息(请求头);
    • 空行;
    • 请求体

image-20240821182449173

响应报文

响应报文格式:

  • 响应首行(响应行); 协议/版本 状态码 状态码描述
  • 响应头信息(响应头);
  • 空行;
  • 响应体

1681525347456

1681525384347

响应状态码:响应码对浏览器来说很重要,它告诉浏览器响应的结果。比较有代表性的响应码如下:

  • 200: 请求成功,浏览器会把响应体内容(通常是html)显示在浏览器中;
  • 302: 重定向,当响应码为302时,表示服务器要求浏览器重新再发一个请求,服务器会发送一个响应头Location指定新请求的URL地址;
  • 304: 使用了本地缓存;
  • 404: 请求的资源没有找到,说明客户端错误的请求了不存在的资源;
  • 405: 请求的方式不允许;
  • 500: 请求资源找到了,但服务器内部出现了错误;

更多的响应状态码:

状态码 英文描述 中文含义
1**
100 Continue 继续。客户端应继续其请求
101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
2**
200 OK 请求成功。一般用于GET与POST请求
201 Created 已创建。成功请求并创建了新的资源
202 Accepted 已接受。已经接受请求,但未处理完成
203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206 Partial Content 部分内容。服务器成功处理了部分GET请求
3**
300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303 See Other 查看其它地址。与301类似。使用GET和POST请求查看
304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305 Use Proxy 使用代理。所请求的资源必须通过代理访问
306 Unused 已经被废弃的HTTP状态码
307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向
4**
400 Bad Request 客户端请求的语法错误,服务器无法理解
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留,将来使用
403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 Method Not Allowed 客户端请求中的方法被禁止
406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求
407 Proxy Authentication Required 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408 Request Time-out 服务器等待客户端发送的请求时间过长,超时
409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
410 Gone 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411 Length Required 服务器无法处理客户端发送的不带Content-Length的请求信息
412 Precondition Failed 客户端请求信息的先决条件错误
413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414 Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理
415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
416 Requested range not satisfiable 客户端请求的范围无效
417 Expectation Failed 服务器无法满足Expect的请求头信息
5**
500 Internal Server Error 服务器内部错误,无法完成请求
501 Not Implemented 服务器不支持请求的功能,无法完成请求
502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求
505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无法完成处理
状态码
	100     正在请求
	200     成功
	300     重定向
	400     前端有问题
	500     后端有问题

Servlet

在前后端开发中,“/”有不同的含义

  • 在前端,"/"通常用来表示网站的根路径。例如,http://example.com/中的"/"指的就是网站的根目录。
  • 在后端,尤其是在路由定义中,"/"也是用来表示路由的起始点

动态资源和静态资源

静态资源:

  • 无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源. 例如:html css js img ,音频文件和视频文件;

动态资源:

  • 需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,运行时动态生成,例如Servlet,Thymeleaf ... ...;
  • 动态资源指的不是视图上的动画效果或者是简单的人机交互效果;

有经过java代码处理的文件资源就叫动态资源(一般都是和后台数据有交互的资源)
没有经过java代码处理就叫静态资源(不管有没有动效)

官方JAVAEEAPI文档下载地址:https://www.oracle.com/java/technologies/javaee/javaeetechnologies.html#javaee8

Servlet简介

Servlet (server applet) 是运行在服务端(Tomcat)的Java小程序,是sun公司提供一套定义动态资源规范;代码层面上Servlet就是一个接口。

  • 用来接收、处理客户端请求、响应给浏览器的动态资源。在整个Web应用中,Servlet主要负责接收处理请求、协同调度功能以及响应数据。我们可以把Servlet称为Web应用中的控制器

  • 不是所有的JAVA类都能用于处理客户端请求,能处理客户端请求并做出响应的一套技术标准就是Servlet。

  • Servlet是运行在服务端的,所以 Servlet必须在WEB项目中开发且在Tomcat这样的服务容器中运行。

请求响应与HttpServletRequest和HttpServletResponse之间的对应关系:

1681699577344

Servlet开发流程

方式一

1.前端发送请求

<a href="Hello">跳转页面</a>

2.在src当中创建一个类并实现Servlet接口重写所有方法

public class Test 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 {

    }

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

    @Override
    public void destroy() {

    }
}

3.在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>
        <!--给HelloWorld起一个别名-->
        <servlet-name>hello</servlet-name>
        <!--说明HelloWorld类全路径名-->
        <servlet-class>com.chs.controller.HelloWorld</servlet-class>
    </servlet>
    <servlet-mapping>
        <!--关联别名和映射路径-->
        <servlet-name>hello</servlet-name>
		<!--映射路径 -> 也就是前端访问(请求)的路径-->
        <url-pattern>/Hello</url-pattern>
        <!--可以为一个Servlet匹配多个不同的映射路径,但是不同的Servlet不能使用相同的url-pattern-->
        <!--
            /        表示通配所有资源,不包括jsp文件
            /*       表示通配所有资源,包括jsp文件
            /a/*     匹配所有以a前缀的映射路径
            *.action 匹配所有以action为后缀的映射路径
        -->
    </servlet-mapping>
</web-app>
  • Servlet并不是文件系统中实际存在的文件或者目录,所以为了能够请求到该资源,需要为其配置映射路径;
  • Servlet的请求映射路径配置在web.xml中;
  • <servlet-name>作为Servlet的别名,可以自己随意定义,见名知意就好;
  • <url-pattern>标签用于定义Servlet的请求映射路径;
  • 一个Servlet可以对应多个不同的<url-pattern>
  • 多个Servlet不能使用相同的<url-pattern>
  • <url-pattern>中可以使用一些通配写法:
    • / 表示通配所有资源,不包括jsp文件;
    • /* 表示通配所有资源,包括jsp文件;
    • /a/* 匹配所有以a前缀的映射路径;
    • *.action 匹配所有以action为后缀的映射路径;

4.测试

image-20240821191150283

方式二

1.前端发送请求

<form action="check" method="post">
    用户名:<input name="username" >
    密码:<input name="password" >
    <button>提交</button>
</form>

2.在src当中创建一个类并继承HttpServlet类重写service方法

public class CheckService extends HttpServlet {
    @Override
    public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (username .equals("chs") && password.equals("123456")){
            res.getWriter().write("OK");
        }else {
            res.getWriter().write("NO");
        }
    }
    
   //或者跟详细一点重写doGet方法(接收get请求)和doPost方法(接收post请求)。
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPut(req, resp);
    }
}
  • 自定义一个类,要继承HttpServlet类;
  • 重写service方法,该方法主要就是用于处理用户请求的服务方法;
  • HttpServletRequest 代表请求对象,是有请求报文经过Tomcat转换而来的,通过该对象可以获取请求中的信息;
  • HttpServletResponse 代表响应对象,该对象会被Tomcat转换为响应的报文,通过该对象可以设置响应中的信息;
  • Servlet对象的生命周期(创建、初始化、处理服务、销毁)是由Tomcat管理的,无需自己new;
  • HttpServletRequest HttpServletResponse 两个对象也是由Tomcat负责转换,在调用service方法时传入给我们用的;

3.在web.xml配置请求的映射路径:

<servlet>
   <servlet-name>check</servlet-name>
    <servlet-class>com.chs.controller.CheckService</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>check</servlet-name>
    <url-pattern>/check</url-pattern>
</servlet-mapping>

4.测试

image-20240821191305218

总结注意:

1.前端请求路径要和xml文件中的 /check 映射路径 --》名一致

2.可以为一个Servlet匹配多个不同的映射路径,但是不同的Servlet不能使用相同的url-pattern

3.<servlet-name>作为Servlet的别名,可以自己随意定义,见名知意就好;

4.<url-pattern>中可以使用一些通配写法:

  • / 表示通配所有资源,不包括jsp文件;
  • /* 表示通配所有资源,包括jsp文件;
  • /a/* 匹配所有以a前缀的映射路径;
  • *.action 匹配所有以action为后缀的映射路径;

5.创建servlet默认是第一次请求访问时候创建(通过配置可以让启动服务器的同时创建和初始化)

6.生命周期是自动执行的一些方法 在某个时间点就会自动执行(构造器、init()、service()、destroy())

7.在servlet当中不要轻易定义需要修改的成员变量(属性) 要定义变量就定义final

请求(Request)与响应(Response)

Request(请求)

Request继承体系

ServletRequest → Java 提供的请求对象根接口

HttpServletRequest →java提供的对Http协议封装的请求对象接口

RequestFacade → Tomcat 定义的实现类

Request获取请求数据

1. 请求行: GET /request-demo/req1?username=zhangsan HTTP/1.1

String getMethod() :获取请求方式: GET

String getContextPath() :获取虚拟目录 ( 项目访问路径 ) : /request-demo

StringBuffer getRequestURL(): 获取 URL( 统一资源定位符 ) :http://localhost:8080/request-demo/req1

String getRequestURI() :获取 URI( 统一资源标识符 ) :/request-demo/req1

String getQueryString() :获取请求参数( GET 方式):username=zhangsan&password=123

2.请求头 

String getHeader(String name) :根据请求头名称,获取值   

3.请求体

ServletInputStream getInputStream() :获取字节输入流

BufferedReader getReader() :获取字符输入流

Request 通用方式获取请求参数

Map<String, String[ ]> getParameterMap() :获取所有参数 Map 集合

String[ ] getParameterValues(String name) :根据名称获取参数值(数组)

String getParameter(String name) :根据名称获取参数值(单个值)

请求转发:一种在服务器内部的资源跳转方式

实现方式:
		Req.getRequestDispatcher(“资源B路径”).forward(req,resp);
请求转发资源间共享数据:使用 Request 对象

存储数据
void setAttribute(String name, Object o) :存储数据到 request 域中

获取数据
Object getAttribute(String name) :根据 key ,获取值

删除数据
void removeAttribute(String name) :根据 key ,删除该键值对

Response(响应)

响应数据分为3部分
1.	响应行: HTTP/1.1 200 OK
Void setStatus(int sc)  //设置响应状态码

2.	响应头: Content-Type:text/html
Void setHeader(String name,String value):设置响应头键值对
resp.setContentType("text/html;charset=utf-8");  设置响应的字符编码

3.	响应体: <html><head></head><body><body></html>
printWriter getWriter() ; 获取字符输出流
ServleOutputStream getOutputStream() ; 获取字节输出流

Response完成重定向:一种资源跳转方式 :状态码302;

1.	设置响应状态码: 302
resp.setStatus(302);

2.	设置响应头(“location”,资源B的访问路径)
resp.setHeader("location","responsedemo2");


***重定向简化操作 resp.sendRedirect(资源B的访问路径)
//简化重定向
resp.sendRedirect("资源B路径");

Response 响应字符数据

1.	通过 Response 对象获取字符输出流
PrintWriter writer = resp.getWriter();
2.	写数据
writer.write("aaa");

中文数据乱码:原因通过 Response 获取的字符输出流默认编码:ISO-8859-1
resp.setContentType("text/html;charset=utf-8"); 写到响应体的上面

Response 响应字节数据

1.	通过 Response 对象获取字节输出流
ServletOutputStream outputStream = resp.getOutputStream();

2.	outputStream.write( 字节数据 ); 写数据

Servlet生命周期

生命周期简介

什么是Servlet的生命周?

  • 应用程序中的对象不仅在空间上有层次结构的关系,在时间上也会因为处于程序运行过程中的不同阶段而表现出不同状态和不同行为,这就是对象的生命周期。
  • 简单的叙述生命周期,就是对象在容器中从开始创建到销毁的过程。

Servlet容器:

  • Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。这一点和我们之前所编写的代码有很大不同。在今后的学习中我们会看到,越来越多的对象交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。

Servlet主要的生命周期执行特点:

生命周期 对应方法 执行时机 执行次数
构造对象 构造器 第一次请求或者容器启动 1
初始化 init() 构造完毕后 1
处理服务 service(HttpServletRequest req,HttpServletResponse resp) 每次请求 多次
销毁 destory() 容器关闭 1

生命周期测试

开发servlet代码:

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletLifeCycle  extends HttpServlet {
    public ServletLifeCycle(){
        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("service方法");
    }
    @Override
    public void destroy() {
        System.out.println("销毁方法");
    }
}

配置Servlet:

   <servlet>
        <servlet-name>servletLifeCycle</servlet-name>
        <servlet-class>com.chs.servlet.ServletLifeCycle</servlet-class>
<!--load-on-startup 如果配置的是正整数则表示容器在启动时就要实例化Servlet, 数字表示的是实例化的顺序 -->
        <load-on-startup>6</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletLifeCycle</servlet-name>
        <url-pattern>/servletLiftCycle</url-pattern>
    </servlet-mapping>

servlet对象启动时机-->默认是发请求的时候才会创建servlet对象,初始化 进行服务

6xml的配置可以让我们的项目启动就初始化创建,

而不是请求的时候才去创建配置的数字代表顺序启动,因为后期可能有多个servlet

生命周期总结

  1. 通过生命周期测试发现Servlet对象在容器中是单例的;
  2. 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程, 但是对象只有一个;
  3. 多个线程可能会使用相同的Servlet对象,所以在Servlet中,我们不要轻易定义一些需要经常修改的成员变量;
  4. load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复;
  5. Tomcat容器中,已经定义了一些随系统启动实例化的Servlet,自定义的Servlet的load-on-startup尽量不要占用数字1-5;
  6. tomcat并发能力:默认150-250 ----->通过jvm调优可以达到1000个

Servlet注解方式配置

@WebServlet注解使用

使用@WebServlet注解替换Servlet配置:

//方式1
@WebServlet(
        name = "userServlet",
        //value = "/user",
        urlPatterns = {"/userServlet1","/userServlet2","/userServlet"},   //路径映射
        initParams = {@WebInitParam(name = "encoding",value = "UTF-8")},  //规定编码方式
        loadOnStartup = 6  //实例化的顺序
)
//方式2
@WebServlet("/user")

public class UserServlet  extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String encoding = getServletConfig().getInitParameter("encoding");
        System.out.println(encoding);
        // 获取请求中的参数
        String username = req.getParameter("username");
        if("atguigu".equals(username)){
            //通过响应对象响应信息
            resp.getWriter().write("NO");
        }else{
            resp.getWriter().write("YES");
        }
    }
}

name默认不写的话就是包名.类名,如果自己写了就是自己写的名字

value和urlpatterns等价,urlpatters可以配置数组,里面可以是多个路径

我们后期常用value,因为后期如果name和loadOnStartup都不写,我们可以把注解简写为@WebServlet("/getMsg"),注解当中只有value属性才能简写

servlet的容器是(tomcat)

servlet可以有多个,那么这多个servlet就需要有人去管理,这个管理者就是servlet容器(topcat容器)

servlet容器从java代码角度理解就是个集合,集合当中存放的是map<servletName,servlet对象>

Spring容器类似,也是一个map的集合,管理的就是Spring的对象

servlet的生命周期我们可以看到就是自动干4件事(标准): 创建对象、初始化、服务业务逻辑、销毁
我们是不会干预的,因为都是固定的部分,servlet容器已经帮我们去执行了

servlet本身也是框架的雏形,公共的部分封装包装框架本身就干了,我们只需要关心我们的业务,重写特定的方法,让框架去执行即可,在这我们只需要重写service方法即可

Servlet继承结构

Servlet 接口

接口及方法说明:

  • Servlet 规范接口,所有的Servlet必须实现
    • public void init(ServletConfig config) throws ServletException;
      • 初始化方法,容器在构造servlet对象后,自动调用的方法,容器负责实例化一个ServletConfig对象,并在调用该方法时传入。
      • ServletConfig对象可以为Servlet 提供初始化参数。
    • public ServletConfig getServletConfig();
      • 获取ServletConfig对象的方法,后续可以通过该对象获取Servlet初始化参数。
    • public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
      • 处理请求并做出响应的服务方法,每次请求产生时由容器调用。
      • 容器创建一个ServletRequest对象和ServletResponse对象,容器在调用service方法时,传入这两个对象。
    • public String getServletInfo();
      • 获取ServletInfo信息的方法。
    • public void destroy();
      • Servlet实例在销毁之前调用的方法。
public class HelloWorld implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

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

    /**
     * 业务代码
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.getWriter().write("hello");
    }

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

    @Override
    public void destroy() {

    }

}

GenericServlet 抽象类

源码解释:

  • GenericServlet 抽象类是对Servlet接口一些固定功能的粗糙实现,以及对service方法的再次抽象声明,并定义了一些其他相关功能方法。
    • private transient ServletConfig config;
      • 初始化配置对象作为属性。
    • public GenericServlet()
      • 构造器,为了满足继承而准备。
    • public void destroy()
      • 销毁方法的平庸实现。
    • public String getInitParameter(String name)
      • 获取初始参数的快捷方法。
    • public Enumeration getInitParameterNames()
      • 返回所有初始化参数名的方法。
    • public ServletConfig getServletConfig()
      • 获取初始Servlet初始配置对象ServletConfig的方法。
    • public ServletContext getServletContext()
      • 获取上下文对象ServletContext的方法。
    • public String getServletInfo()
      • 获取Servlet信息的平庸实现。
    • public void init(ServletConfig config) throws ServletException()
      • 初始化方法的实现,并在此调用了init的重载方法。
    • public void init() throws ServletException
      • 重载init方法,为了让我们自己定义初始化功能的方法。
    • public void log(String msg)
    • public void log(String message, Throwable t)
      • 打印日志的方法及重载。
    • public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
      • 服务方法再次声明。
    • public String getServletName()
      • 获取ServletName的方法。
public class GenericServlet extends jakarta.servlet.GenericServlet {
    /**
     * 业务代码
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }
}

servlet机制给我们提供了GeneracServlet类,它已经实现了Servlet接口,所以当我们只关心业务逻辑的时候,继承GeneracServlet类,直接重写service方法即可

GenericServlet是Servlet的一个实现类,已经对Servlet的接口方法做了实现,但是是抽象类,无法去创建对象

HttpServlet 抽象类

解释:

  • abstract class HttpServlet extends GenericServlet HttpServlet抽象类,除了基本的实现以外,增加了更多的基础功能。
    • private static final String METHOD_DELETE = "DELETE";
    • private static final String METHOD_HEAD = "HEAD";
    • private static final String METHOD_GET = "GET";
    • private static final String METHOD_OPTIONS = "OPTIONS";
    • private static final String METHOD_POST = "POST";
    • private static final String METHOD_PUT = "PUT";
    • private static final String METHOD_TRACE = "TRACE";
      • 上述属性用于定义常见请求方式名常量值。
    • public HttpServlet()
      • 构造器,用于处理继承。
    • public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
      • 对服务方法的实现。
      • 在该方法中,将请求和响应对象转换成对应HTTP协议的HttpServletRequest HttpServletResponse对象。
      • 调用重载的service方法。
    • public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
      • 重载的service方法,被重写的service方法所调用。
      • 在该方法中,通过请求方式判断,调用具体的do***方法完成请求的处理。
    • protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    • protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    • protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    • protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    • protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    • protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    • protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
      • 对应不同请求方式的处理方法。
      • 除了doOptions和doTrace方法,其他的do*** 方法都在故意响应错误信息。
public class HttpServlet extends jakarta.servlet.http.HttpServlet {

    /**
     * 方式一 根据请求方式做出对应的回应
     **/
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

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

    /**
     * 方式二 要么就要重写httpservlet自己写的那个service方法 ****
     **/
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
    }
}

HttpServlet类当中有两个sevice方法,一个是自己的service方法 一个是重写的service方法,

当请求的时候会自动调用重写的service方法,重写的service方法只是对参数做了个强转(向下转型),

进而调用自己的service方法,自己的service方法内部就对请求方式做了判断,调用不同的doXXX(doGet,doPost,doDelete,doPut......)

自定义Servlet

继承关系图解:

1682299663047

  • 自定义Servlet中,必须要对处理请求的方法进行重写:
    • 要么重写service方法;
    • 要么重写doGet/doPost方法;

servlet映射路径问题

​ / 表示通配所有资源,不包括jsp文

​ /* 表示通配所有资源,包括jsp文件

完全路径匹配
	请求路径: adduser
	拦截路径:/adduser
	
目录路径匹配(按照功能模块匹配)
	请求路径:user/adduser   user/login  user/register user/update
	拦截路径:/user/*    @WebServlet("/user/*")   (后期用的最多)
	
扩展名匹配 
	*.action    前面任意 后面以action结尾的

ServletConfig和ServletContext

ServletConfig[Servlet的初始配置]

ServletConfig是什么?

  • 为Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象;
  • 容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性;
1682302307081

ServletConfig是一个接口,定义了如下API:

package jakarta.servlet;
import java.util.Enumeration;
public interface ServletConfig {
    String getServletName();
    
    ServletContext getServletContext();
    
    String getInitParameter(String var1);
    
    Enumeration<String> getInitParameterNames();
}
方法名 作用
getServletName() 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称。
getServletContext() 获取ServletContext对象。
getInitParameter() 获取配置Servlet时设置的『初始化参数』,根据名字获取值。
getInitParameterNames() 获取所有初始化参数名组成的Enumeration对象。

ServletConfig怎么用,测试代码如下:

方式一【xml文件中写】

  • 定义Servlet
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletConfig servletConfig = this.getServletConfig();
        // 根据参数名获取单个参数
        String value = servletConfig.getInitParameter("param1");
        System.out.println("param1:"+value);
        // 获取所有参数名
        Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
        // 迭代并获取参数名
        while (parameterNames.hasMoreElements()) {
            String paramaterName = parameterNames.nextElement();
            System.out.println(paramaterName+":"+servletConfig.getInitParameter(paramaterName));
        }
    }
}

配置Servlet(配置xml文件)

<servlet>
   <servlet-name>ServletA</servlet-name>
   <servlet-class>com.atguigu.servlet.ServletA</servlet-class>
    <!--配置ServletA的初始参数-->
    <init-param>
       <param-name>param1</param-name>
       <param-value>value1</param-value>
    </init-param>
    <init-param>
       <param-name>param2</param-name>
       <param-value>value2</param-value>
    </init-param>
</servlet>
<servlet-mapping>
     <servlet-name>ServletA</servlet-name>
     <url-pattern>/servletA</url-pattern>
</servlet-mapping>

方式二【注解中写】

  • 定义servlet
//注解的写法
	@WebServlet(value = "/myconfig",initParams = {
		 @WebInitParam(name="name",value="zhaoliying"),
		 @WebInitParam(name="age",value="32")
})
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletConfig servletConfig = this.getServletConfig();
        // 根据参数名获取单个参数
        String value = servletConfig.getInitParameter("param1");
        System.out.println("param1:"+value);
        // 获取所有参数名
        Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
        // 迭代并获取参数名
        while (parameterNames.hasMoreElements()) {
            String paramaterName = parameterNames.nextElement();
            System.out.println(paramaterName+":"+servletConfig.getInitParameter(paramaterName));
        }
    }
}

ServletContext[上下文对象-共享]

ServletContext是什么?

  • ServletContext对象有称呼为上下文对象,或者叫应用域对象(后面统一讲解域对象);
  • 容器会为每个app创建一个独立的唯一的ServletContext对象;
  • ServletContext对象为所有的Servlet所共享
  • ServletContext可以为所有的Servlet提供初始配置参数;
  • ServletContext的初始参数配置只能在xml文件中写
1682303205351

ServletContext怎么用?

  • 配置ServletContext参数
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0">
    <context-param>
        <param-name>paramA</param-name>
        <param-value>valueA</param-value>
    </context-param>
    <context-param>
        <param-name>paramB</param-name>
        <param-value>valueB</param-value>
    </context-param>
</web-app>
  • 在Servlet中获取ServletContext并获取参数
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
        // 从ServletContext中获取为所有的Servlet准备的参数
        ServletContext servletContext = this.getServletContext();
        
        
        String valueA = servletContext.getInitParameter("paramA");
        System.out.println("paramA:"+valueA);
        // 获取所有参数名
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        // 迭代并获取参数名
        while (initParameterNames.hasMoreElements()) {
            String paramaterName = initParameterNames.nextElement();
            System.out.println(paramaterName+":"+servletContext.getInitParameter(paramaterName));
        }
    }
}

获取servletContext的方式

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1
ServletContext servletContext = this.getServletConfig().getServletContext();
//2
ServletContext servletContext1 = this.getServletContext();
//3
ServletContext servletContext2 = req.getServletContext();
}

ServletContext其他重要API

获取资源的磁盘路径:

//动态获取资源的真实路径
String realPath = servletContext.getRealPath("资源在web目录中的路径");

String realPath = servletContext.getRealPath("index.html");

获取项目的上下文路径:

//动态获取项目真实的上下文路径( 项目访问路径 )
String contextPath = servletContext.getContextPath();

把项目当中的文件变为流对象

servletContext.getResourceAsStream("jdbc.properties");
this.getClass().getClassLoader().getResourceAsStream()

应用域

  • 定义:应用域是在整个 Web 应用程序的生命周期内有效的作用域。它可以在 ServletContext 中访问。
  • 特点:在应用域中存储的数据对整个 Web 应用程序中的所有 Servlet 和 JSP 都是可见的,并且在应用程序运行期间一直有效,直到应用程序被销毁。
存储数据
void setAttribute(String name, Object o) :存储数据到 ServletContext 域中

获取数据
Object getAttribute(String name) :根据 key ,获取值

删除数据
void removeAttribute(String name) :根据 key ,删除该键值对

域对象的相关API:

  • 域对象:一些用于在一些特定的范围内存储数据和传递数据的对象,不同的范围称为不同的“域”,不同的域对象代表不同的域,共享数据的范围也不同;
  • ServletContext代表应用,所以ServletContext域也叫作应用域,是webapp中最大的域,可以在本应用内实现数据的共享和传递;
  • webapp中的三大域对象,分别是应用域会话域请求域。后续我们会将三大域对象统一进行讲解和演示;
  • 三大域对象都具有的API如下:
API 功能解释
void setAttribute(String key,Object value); 向域中存储/修改数据
Object getAttribute(String key); 获得域中的数据
void removeAttribute(String key); 移除域中的数据

HttpServletRequest

HttpServletRequest是什么?

  • HttpServletRequest是一个接口,其父接口是ServletRequest;
  • HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入;
  • HttpServletRequest代表客户端发来的请求,请求中的所有信息都可以通过该对象获得;

image-20240823173305918

HttpServletRequest常见API

HttpServletRequest怎么用?

  • 获取请求行信息相关(方式,请求的url,协议及版本):
API 功能解释
StringBuffer getRequestURL(); 获取客户端请求的url[获取 URL( 统一资源定位符 )]
String getRequestURI(); 获取客户端请求项目中的具体资源[获取 URI( 统一资源标识符 )]
int getServerPort(); 获取客户端发送请求时的端口。
int getLocalPort(); 获取本应用在所在容器的端口。
int getRemotePort(); 获取客户端程序的端口。
String getScheme(); 获取请求协议。
String getProtocol(); 获取请求协议及版本号。
String getMethod(); 获取请求方式。
  • 获得请求头信息相关:
API 功能解释
String getHeader(String headerName); 根据头名称获取请求头。
Enumeration getHeaderNames(); 获取所有的请求头名字。
String getContentType(); 获取content-type请求头。
  • 获得请求参数相关:
API 功能解释
String getParameter(String parameterName); 根据请求参数名获取请求单个参数值。
String[] getParameterValues(String parameterName); 根据请求参数名获取请求多个参数值数组。
Enumeration getParameterNames(); 获取所有请求参数名。
Map<String, String[]> getParameterMap(); 获取所有请求参数的键值对集合。
BufferedReader getReader() throws IOException; 获取读取请求体的字符输入流。
ServletInputStream getInputStream() throws IOException; 获取读取请求体的字节输入流。
int getContentLength(); 获得请求体长度的字节数。
  • 其他API:
API 功能解释
String getServletPath(); 获取请求的Servlet的映射路径。
ServletContext getServletContext(); 获取ServletContext对象。
Cookie[] getCookies(); 获取请求中的所有cookie。
HttpSession getSession(); 获取Session对象。
void setCharacterEncoding(String encoding) ; 设置请求体字符集。

请求域(quest域)

  • 定义:请求域是在单个 HTTP 请求期间有效的作用域。它可以在 HttpServletRequest 对象中访问。
  • 特点:在请求域中存储的数据仅在当前请求期间有效,一旦请求被处理完成,数据将不再可用。适用于需要在请求处理过程中临时存储数据。
存储数据
void setAttribute(String name, Object o) :存储数据到 request 域中

获取数据
Object getAttribute(String name) :根据 key ,获取值

删除数据
void removeAttribute(String name) :根据 key ,删除该键值对

HttpServletResponse

HttpServletResponse是什么?

  • HttpServletResponse是一个接口,其父接口是ServletResponse;
  • HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入;
  • HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息;

image-20240823174255631

HttpServletResponse的常见API

HttpServletRequest怎么用?

  • 设置响应行相关:
API 功能解释
void setStatus(int code); 设置响应状态码
  • 设置响应头相关:
API 功能解释
void setHeader(String headerName, String headerValue); 设置/修改响应头键值对
void setContentType(String contentType); 设置content-type响应头及响应字符集(设置MIME类型)
  • 设置响应体相关:
API 功能解释
PrintWriter getWriter() throws IOException; 获得向响应体放入信息的字符输出流
ServletOutputStream getOutputStream() throws IOException; 获得向响应体放入信息的字节输出流
void setContentLength(int length); 设置响应体的字节长度,其实就是在设置content-length响应头
  • 其他API:
API 功能解释
void sendError(int code, String message) throws IOException; 向客户端响应错误信息的方法,需要指定响应码和响应信息
void addCookie(Cookie cookie); 向响应体中增加cookie
void setCharacterEncoding(String encoding); 设置响应体字符集

MIME类型:

  • MIME类型,可以理解为文档类型,用户表示传递的数据是属于什么类型的文档;
  • 浏览器可以根据MIME类型决定该用什么样的方式解析接收到的响应体数据;
  • 可以这样理解::前后端交互数据时,告诉对方发给对方的是 html/css/js/图片/声音/视频/... ...;
  • tomcat/conf/web.xml中配置了常见文件的拓展名和MIMIE类型的对应关系;
  • 常见的MIME类型举例如下:
文件拓展名 MIME类型
.html text/html
.css text/css
.js application/javascript
.json application/json
.png /.jpeg/.jpg/... ... image/jpeg
.mp3/.mpe/.mpeg/ ... ... audio/mpeg
.mp4 video/mp4
.m1v/.m1v/.m2v/.mpe/... ... video/mpeg

请求转发和响应重定向

什么是请求转发和响应重定向?

  • 请求转发和响应重定向是web应用中间接访问项目资源的两种手段,也是Servlet控制页面跳转的两种手段;
  • 请求转发通过HttpServletRequest实现,响应重定向通过HttpServletResponse实现;

请求转发

image-20240823174626194

请求转发特点(背诵):

  • 请求转发通过HttpServletRequest对象获取请求转发器实现;
  • 请求转发是服务器内部的行为,对客户端是屏蔽的;
  • 客户端只发送了一次请求,客户端地址栏不变;
  • 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源;
  • 因为全程只有一个HttpServletRequset对象,所以请求参数可以传递,请求域中的数据也可以传递;
  • 请求转发可以转发给其他Servlet动态资源,也可以转发给一些静态资源以实现页面跳转
  • 请求转发可以转发给WEB-INF下受保护的资源
  • 请求转发不能转发到本项目以外的外部资源

例: ---》 req.getRequestDispatcher("/servletB").forward(req,resp)

//servletA
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //  获取请求转发器
        RequestDispatcher  requestDispatcher = req.getRequestDispatcher("/servletB");
        //  做出转发动作
        requestDispatcher.forward(req,resp);
        
        //  转发给WEB-INF下的受保护的资源  ok
        //RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/views/view1.html");
         //requestDispatcher.forward(req,resp)
    }
}
//servletB
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 做出响应
        resp.getWriter().write("servletB response");        
    }
}
  • 打开浏览器,输入以下url测试
http://localhost:8080/web03_war_exploded/servletA

响应重定向

响应重定向运行逻辑图:

1682322460011

响应重定向特点(背诵):

  • 响应重定向通过HttpServletResponse对象的sendRedirect方法实现;
  • 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的客户端的行为;
  • 客户端至少发送了两次请求,客户端地址栏是要变化的;
  • 服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源
  • 因为全程产生了多个HttpServletRequset对象,所以请求参数不可以传递,请求域中的数据也不可以传递;
  • 重定向可以是其他Servlet动态资源,也可以是一些静态资源以实现页面跳转;
  • 重定向不可以到给WEB-INF下受保护的资源
  • 重定向可以到本项目以外的外部资源
  • java代码当中"/"代表的是[上下文路径],注意只有重定向不一样,重定向里面所写的/和前端代码一样

:

//servletA
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //  响应重定向
        // 重定向到servlet动态资源 OK
		//java代码当中"/"代表的是上下文路径,注意只有重定向不一样,重定向里面所写的/和前端代码一样
        resp.sendRedirect("servletB");
        
        // 重定向到外部资源
        //resp.sendRedirect("http://www.baidu.com");
    }
}
//servletB
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 做出响应
        resp.getWriter().write("servletB response");

    }
}
  • 打开浏览器,输入以下url测试
http://localhost:8080/web03_war_exploded/servletA

转发和重定向的总结

描述 转发 重定向
浏览器感知方面 感知不到服务器内部操作 可以感知返回浏览器302
浏览器地址变化方面 不会发生变化 会发生变化
整个过程发送请求次数方面 1次 至少两次
能否共享request对象数据 可以 不可以
能否访问WEB-INF的资源 可以 不可以
访问的目标资源是否可以是外部资源 不可以 可以

注意:旧业务结束新业务开始就重定向,旧业务延续采用转发

web乱码和路径问题总结

乱码问题

乱码问题产生的根本原因是什么?

  1. 数据的编码和解码使用的不是同一个字符集
  2. 使用了不支持某个语言文字的字符集

各个字符集的兼容性:

1682326867396
  • 由上图得知,上述字符集都兼容了ASCII;
  • ASCII中有什么? 英文字母和一些通常使用的符号,所以这些东西无论使用什么字符集都不会乱码;

HTML乱码问题

设置项目文件的字符集要使用一个支持中文的字符集。

  • 查看当前文件的字符集。
1682325817829
  • 查看项目字符集 配置,将Global Encoding 全局字符集,Project Encoding 项目字符集,Properties Files 属性配置文件字符集设置为UTF-8。
1682326229063

当前视图文件的字符集通过<meta charset="UTF-8"> 来告知浏览器通过什么字符集来解析当前文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    中文
</body>
</html>

Tomcat控制台乱码

在tomcat10.1.7这个版本中,修改 Tomcat/conf/logging.properties中,所有的UTF-8为GBK即可。

sout乱码问题,设置JVM加载.class文件时使用UTF-8字符集。

  • 设置虚拟机加载.class文件的字符集和编译时使用的字符集一致。
1695189588009

请求乱码问题

1.GET请求乱码

GET请求方式乱码分析:

  • GET方式提交参数的方式是将参数放到URL后面,如果使用的不是UTF-8,那么会对参数进行URL编码处理;
  • HTML中的 <meta charset='字符集'/> 影响了GET方式提交参数的URL编码;
  • Tomcat10.1.7的URI编码默认为 UTF-8;
  • 当GET方式提交的参数URL编码和Tomcat10.1.7默认的URI编码不一致时,就会出现乱码;

GET请求方式乱码演示:

  • 浏览器解析的文档的<meta charset="GBK" />

1682385870660

  • GET方式提交时,会对数据进行URL编码处理 ,是将GBK 转码为 "百分号码"。
1682385997927
  • Tomcat10.1.7 默认使用UTF-8对URI进行解析,造成前后端使用的字符集不一致,出现乱码。

1682386110151

GET请求方式乱码解决:

  • 方式1 :设置GET方式提交的编码和Tomcat10.1.7的URI默认解析编码一致即可 (推荐)。
1682386298048
  • 方式2 : 设置Tomcat10.1.7的URI解析字符集和GET请求发送时所使用URL转码时的字符集一致即可,修改conf/server.xml中 Connecter 添加 URIEncoding="GBK" (此时不推荐)。
1682386551684 1682386611945

2 POST方式请求乱码

POST请求方式乱码分析:

  • form表单的POST请求将参数放在请求体中进行发送;
  • 请求体使用的字符集受到了<meta charset="字符集"/> 的影响;
  • Tomcat10.1.7 默认使用UTF-8字符集对请求体进行解析;
  • 如果请求体的URL转码和Tomcat的请求体解析编码不一致,就容易出现乱码;

POST方式乱码演示:

  • POST请求请求体受到了<meta charset="字符集"/> 的影响。
1682387258428
  • 请求体中,将GBK数据进行 URL编码。

1682387349916

  • 后端默认使用UTF-8解析请求体,出现字符集不一致,导致乱码。
1682387412704

POST请求方式乱码解决:

  • 方式1 : 请求时,使用UTF-8字符集提交请求体 (推荐)。
1682387836615 1682387857587
  • 方式2 : 后端在获取参数前,设置解析请求体使用的字符集和请求发送时使用的字符集一致 (此时不推荐)。

1682388026978

响应乱码问题

响应乱码分析:

  • 在Tomcat10.1.7中,向响应体中放入的数据默认使用了工程编码 UTF-8。
  • 浏览器在接收响应信息时,使用了不同的字符集或者是不支持中文的字符集就会出现乱码。

响应乱码演示:

  • 服务端通过response对象向响应体添加数据。
1682388204239
  • 浏览器接收数据解析乱码。
1682388599014

响应乱码解决:

  • 方式1 :手动设定浏览器对本次响应体解析时使用的字符集(不推荐)。

    • edge和 chrome浏览器没有提供直接的比较方便的入口,不方便;
  • 方式2:后端通过设置响应体的字符集和浏览器解析响应体的默认字符集一致(此时不推荐)。

1682389063225

方式3: 通过设置Content-Type响应头,告诉浏览器以指定的字符集解析响应体(推荐)。

1682389263627

1682389317234

路径问题

相对路径和绝对路径:

  • 相对路径:

    • 相对路径的规则是以当前资源所在的路径为出发点去寻找目标资源
    • 相对路径不以 / 开头
    • 在file协议下,使用的是磁盘路径;
    • 在http协议下,使用的是url路径;
    • 相对路径中可以使用 ./表示当前资源所在路径,可以省略不写;
    • 相对路径中可以使用../表示当前资源所在路径的上一层路径,需要时要手动添加;
  • 绝对路径:

    • 绝对路径的规则是: 使用以一个固定的路径做出出发点去寻找目标资源,和当前资源所在的路径没有关系;
    • 绝对路径要以/ 开头
    • 绝对路径的写法中,不以当前资源的所在路径为出发点,所以不会出现 ./ 和../开头;
    • 不同的项目和不同的协议下,绝对路径的基础位置可能不同,要通过测试确定;
    • 绝对路径的好处就是,无论当前资源位置在哪,寻找目标资源路径的写法都一致;
  • 应用场景:

    1. 前端代码中,href、src、action 等属性;
    2. 请求转发和重定向中的路径;

base标签的使用

base标签定义页面相对路径公共前缀:

  • base 标签定义在head标签中,用于定义相对路径的公共前缀;
  • base 标签定义的公共前缀只在相对路径上有效,绝对路径中无效;
  • 如果相对路径开头有 ./ 或者../修饰,则base标签对该路径同样无效;

index.html 和a/b/c/test.html 以及view1Servlet 中的路径处理:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!--定义相对路径的公共前缀,将相对路径转化成了绝对路径-->
    <base href="/web03_war_exploded/">
</head>
<body>
    <img src="static/img/logo.png">
</body>
</html>

缺省项目上下文路径

项目上下文路径变化问题:

  • 通过 base标签虽然解决了相对路径转绝对路径问题,但是base中定义的是项目的上下文路径
  • 项目的上下文路径是可以随意变化的;
  • 一旦项目的上下文路径发生变化,所有base标签中的路径都需要改;

解决方案:

  • 将项目的上下文路径进行缺省设置,设置为 / ,则所有的绝对路径中就不必填写项目的上下文了,直接就是/开头即可。

前端路径和后端路径的区别

前端 
	/代表的是localhost:8080  是缺少contextPath的

后端
	/代表的是 /contextPath 不缺上下文路径

特别注意:如果是重定向,重定向和前端一样也是缺少上下文路径的

会话_过滤器_监听器

会话

为什么需要会话管理【保持状态-数据持久化】

HTTP是无状态协议:

  • 无状态就是不保存状态,即无状态协议(stateless),HTTP协议自身不对请求和响应之间的通信状态进行保存,也就是说,在HTTP协议这个级别,协议对于发送过的请求或者响应都不做持久化处理;

  • 简单理解:浏览器发送请求,服务器接收并响应,但是服务器不记录请求来自哪个浏览器,服务器没记录浏览器的特征,就是客户端的状态;

举例:张三去一家饭馆点了几道菜,觉得味道不错,第二天又去了,对老板说,还点上次的那几道菜。

  • 无状态:老板没有记录张三是否来过,更没有记录上次他点了那些菜,张三只能重新再点一遍;
  • 有状态:老板把每次来吃饭的用户都做好记录,查阅一下之前的记录,查到了张三之前的菜单,直接下单;

会话管理实现的手段

Cookie和Session配合解决:

  • cookie是在客户端保留少量数据的技术,主要通过响应头向客户端响应一些客户端要保留的信息;
  • session是在服务端保留更多数据的技术,主要通过服务端HttpSession对象保存一些和客户端相关的信息;
  • cookie和session配合记录请求状态;

举例:张三去银行办业务。

  • 张三第一次去某个银行办业务,银行会为张三开户(Session),并向张三发放一张银行卡(Cookie);
  • 张三后面每次去银行,就可以携带之间的银行卡(Cookie),银行根据银行卡找到之前张三的账户(Session);

Cookie是一种客户端会话技术,Cookie由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去:

  • 服务端创建Cookie,将Cookie放入响应对象中,Tomcat容器将Cookie转化为set-cookie响应头,响应给客户端;
  • 客户端在收到Cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie;
  • Cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐;
  • 由于Cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据;

1682411089082

应用场景举例:

  1. 记录用户名;
  2. 保存电影播放进度;

servletA向响应中增加Cookie:

@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建Cookie
        Cookie cookie1 =new Cookie("c1","c1_message");
        Cookie cookie2 =new Cookie("c2","c2_message");
        // 将cookie放入响应对象
        resp.addCookie(cookie1);
        resp.addCookie(cookie2);
    }
}

servletB从请求中读取Cookie:

@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求中的cookie
        Cookie[] cookies = req.getCookies();
        //迭代cookies数组
        if (null != cookies && cookies.length!= 0) {
            for (Cookie cookie : cookies) {
                System.out.println(cookie.getName()+":"+cookie.getValue());
            }
        }
    }
}

默认情况下Cookie的有效期是一次会话范围内,我们可以通过Cookie的setMaxAge()方法让Cookie持久化保存到浏览器上。

  • 会话级Cookie:
    • 服务器端并没有明确指定Cookie的存在时间;
    • 在浏览器端,Cookie数据存在于内存中;
    • 只要浏览器还开着,Cookie数据就一直都在;
    • 浏览器关闭,内存中的Cookie数据就会被释放;
  • 持久化Cookie:
    • 服务器端明确设置了Cookie的存在时间;
    • 在浏览器端,Cookie数据会被保存到硬盘上;
    • Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响;
    • 持久化Cookie到达了预设的时间会被释放;

cookie.setMaxAge(int expiry)参数单位是秒,表示cookie的持久化时间,如果设置参数为0,表示将浏览器中保存的该cookie删除。

  • servletA设置一个Cookie为持久化cookie。
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建Cookie
        Cookie cookie1 =new Cookie("c1","c1_message");
        cookie1.setMaxAge(60); 时间是秒
        Cookie cookie2 =new Cookie("c2","c2_message");
        // 将cookie放入响应对象
        resp.addCookie(cookie1);
        resp.addCookie(cookie2);
    }
}

cookie.setMaxAge(value)
value = 10 代表10秒,如果想持久化cookie就把时间设置长
value = 0 代表删除cookie
value = -1 代表浏览器关闭会话结束就删除 默认值

Cookie的提交路径(cookie对哪些路径有效)

访问互联网资源时不需要每次都需要把所有Cookie带上。访问不同的资源时,可以携带不同的cookie,可以通过Cookie的setPath(String path) 对Cookie的路径进行设置。

  • 从ServletA中获取cookie。
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建Cookie
        Cookie cookie1 =new Cookie("c1","c1_message");
        // 设置cookie的提交路径
        cookie1.setPath("/web03_war_exploded/servletB");
        Cookie cookie2 =new Cookie("c2","c2_message");
        // 将cookie放入响应对象
        resp.addCookie(cookie1);
        resp.addCookie(cookie2);
    }
}

注意:getPath返回的是null 因为浏览器带过来的只有name和value,而我们设置的这些属性都是为了让浏览器去组织保存cookie使用的

cookie.setPath(上下文路径+servlet路径)

cookie

cookie
	是什么
		cookie是存储在前端本地的一小段文本信息,大小约为4k
		cookie是由后端服务器创建,通过设置响应头set-cookie,把信息携带到浏览器
		浏览器在看到有这样的响应头,就会把对应的信息,存储到本地的cookie文本当中
		后续浏览器如果还是给本域发请求,就会默认在请求头携带这个cookie信息到服务器
		服务器获取到这个cookie就可以知道是不是同一个客户端

cookie的设置
		cookie存储的键值对都是字符串
		cookie的创建
			Cookie.cookie =  new Cookie("键名","值");直接创建键值对对象
			如果要设置多个cookie,需要去new多次
		
		cookie对哪些路径有效
			设置了后期不同的路径下浏览器传递cookie是不一样的
        cookie1.setPath("/web03_war_exploded/servletB");
        
		cookie的超时时间(时效性、有效期)
			cookie.setMaxAge(value)
			value = 10  代表10秒,如果想持久化cookie就把时间设置长
			value = 0   代表删除cookie
			value = -1  代表浏览器关闭会话结束就删除   默认值

			持久化cookie   cookie.setMaxAge(3600 * 24 * 365 * 100)

		cookie通过resp对象传递到客户端   返回的响应头里面会带上set-cookie字段,浏览器就会根据这个去保存
			resp.addCookie(cookie)

	cookie的获取
		从前端请求带上cookie,后台需要把前端带过来的cookie进行获取解析
		req.getCookies() 会返回cookie的数组
		通过遍历获取每个cookie对象,然后通过cookie对象的
		getName
		getValue
		
		注意:getPath返回的是null  因为浏览器带过来的只有name和value,而我们设置的这些属性
		都是为了让浏览器去组织保存cookie使用的

session

HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象.。客户端在发送请求时,都可以使用自己的session。这样服务端就可以通过session来记录某个客户端的状态了。

  • 服务端在为客户端创建session时,会同时将session对象的id,即JSESSIONID以Cookie的形式放入响应对象
  • 后端创建完session后,客户端会收到一个特殊的Cookie,叫做JSESSIONID
  • 客户端下一次请求时携带JSESSIONID,后端收到后,根据JSESSIONID找到对应的session对象;
  • 通过该机制,服务端通过session就可以存储一些专门针对某个客户端的信息了;

原理图如下:

1682413051408

应用场景:

  1. 记录用户的登录状态:

    用户登录后,将用户的账号等敏感信息存入session;

  2. 记录用户操作的历史:

    例如记录用户的访问痕迹,用户的购物车信息等临时性的信息;

HttpSession的使用

  • 定义ServletA,将用户名存入session。
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取请求中的参数
        String username = req.getParameter("username");
        // 获取session对象
        HttpSession session = req.getSession();
         // 获取Session的ID
        String jSessionId = session.getId();
        System.out.println(jSessionId);
        // 判断session是不是新创建的session
        boolean isNew = session.isNew();
        System.out.println(isNew);
        // 向session对象中存入数据
        session.setAttribute("username",username);
    }
}
  • 定义ServletB,从session中读取用户名。
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取session对象
        HttpSession session = req.getSession();
         // 获取Session的ID
        String jSessionId = session.getId();
        System.out.println(jSessionId);
        // 判断session是不是新创建的session
        boolean isNew = session.isNew();
        System.out.println(isNew);
        // 从session中取出数据
        String username = (String)session.getAttribute("username");
        System.out.println(username);
    }
}

getSession方法的处理逻辑:

1682477914654

HttpSession时效性

默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在Tomcat/conf/web.xml配置为30分钟。

也可以通过HttpSession的API 对最大闲置时间进行设定。

// 设置最大闲置时间
session.setMaxInactiveInterval(5); //是两次使用session的间隔超过5秒就过期

也可以直接让session失效。

// 直接让session失效
session.invalidate();  //[用于退出登录]

session总结

session

session是存储在服务器端的信息,当用户客户端第一次访问服务器,服务器可以给这个客户端创建一个session对象
session对象有一个id,然后通过cookie把创建好的session对象的id组成键值对,传递到前端(set-cookie),前端在看到的时候,就会把JSessionId:id的值,保存到cookie,后续只要发请求,前端都会把这个键值对放在cookie当中通过请求头传递到后端,后端就可以根据传递过来的id去找到session当中保存的数据,转化为各种api去操作

session域【会话域】

在Servlet技术中,会话域(session domain)指的是通过HttpSession对象所创建的一个用户会话的范围。这个范围用于在用户与服务器之间的多次请求之间共享数据,即在一次会话期间,用户可以在不同的页面之间传递和共享信息。

存储数据
void setAttribute(String name, Object o) :存储数据到 ServletContext 域中

获取数据
Object getAttribute(String name) :根据 key ,获取值

删除数据
void removeAttribute(String name) :根据 key ,删除该键值对

cookie和session的区别

大小 
		cookie最大4k
		session无限制 主要看硬件的支持
安全
		session比cookie安全
		cookie存的是明文不加密
		session存储的是一个对象的id 而且还加密
存储的信息 
		cookie存储键值对
		
		session存储的是个对象

有效期
		cookie 默认是关闭浏览器就失效 可以设置持久化存储  cookie.setMaxAge()
		
		session 默认是30分钟,浏览器不关闭 两次访问间隔 
		session.setMaxInactiveInterval(5); 设置超时时间5秒  是两次使用session的间隔超过5秒就过期
		session.invalidate();删除session*********

三大域对象

域对象概述

域对象:一些用于存储数据和传递数据的对象。传递数据不同的范围,我们称之为不同的域。不同的域对象代表不同的域,共享数据的范围也不同。

  • web项目中,我们一定要熟练使用的域对象分别是 :请求域、会话域、应用域;
  • 请求域对象是HttpServletRequest ,传递数据的范围是一次请求之内及请求转发;
  • 会话域对象是HttpSession,传递数据的范围是一次会话之内,可以跨多个请求;
  • 应用域对象是ServletContext,传递数据的范围是本应用之内,可以跨多个会话;

域对象的API:

API 功能
void setAttribute(String name,String value) 向域对象中添加/修改数据
Object getAttribute(String name); 从域对象中获取数据
void removeAttribute(String name); 移除域对象中的数据
  • ServletA向三大域中放入数据。
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 向请求域中放入数据
        req.setAttribute("request","request-message");
        //req.getRequestDispatcher("servletB").forward(req,resp);
        // 向会话域中放入数据
        HttpSession session = req.getSession();
        session.setAttribute("session","session-message");
        // 向应用域中放入数据
        ServletContext application = getServletContext();
        application.setAttribute("application","application-message");
    }
}
  • ServletB从三大于中取出数据。
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 从请求域中获取数据
        String reqMessage =(String)req.getAttribute("request");
        System.out.println(reqMessage); 
        // 从会话域中获取数据
        HttpSession session = req.getSession();
        String sessionMessage =(String)session.getAttribute("session");
        System.out.println(sessionMessage);
        // 从应用域中获取数据
        ServletContext application = getServletContext();
        String applicationMessage =(String)application.getAttribute("application");
        System.out.println(applicationMessage);
    }
}
  • 请求转发时,请求域可以传递数据。请求域内一般放本次请求业务有关的数据,如:查询到的所有的部门信息
  • 同一个会话内,不用请求转发,会话域可以传递数据。会话域内一般放本次会话的客户端有关的数据,如:当前客户端登录的用户
  • 同一个APP内,不同的客户端,应用域可以传递数据。应用域内一般放本程序应用有关的数据 如:Spring框架的IOC容器

Filter[过滤器]

过滤器概述

Filter,即过滤器,是JAVAEE技术规范之一,作为目标资源的请求进行过滤的一套技术规范,是Java Web项目中最为实用的技术之一

filter过滤器是放在servlet之前的,可以控制客户端是否可以访问servlet

  • Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口
  • Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后,会先调用Filter的doFilter方法;
  • Filter的doFilter方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应;
  • Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理;
  • Filter是GOF中责任链模式的典型案例;
  • Filter的常用应用包括但不限于:登录权限检查、解决网站乱码、过滤敏感字符、日志记录、性能分析... ...;
  • [Filtet接口的实现类GenericFilter,GenericFilter的子类是 HttpFilter。跟servlet接口一样]
  • API目标
API 目标
default public void init(FilterConfig filterConfig) 初始化方法,由容器调用并传入初始配置信息filterConfig对象
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中
default public void destroy() 销毁方法,容器在回收过滤器对象之前调用的方法

过滤器使用

如果不放行代表拦截,那么我们都是配合重定向去使用
放行的话,那么需要把request和response继续往后传递

一、xml文件配置映射方式配置过滤器

//创建过滤器类
public class FilterTest extends HttpFilter {
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("过滤器");
        chain.doFilter(request,response);  //放行
    }
}
//创建servlet
@WebServlet("/filterTest")
public class FilterTest extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("test");
    }
}
<!--配置xml文件映射-->
<filter>
        <filter-name>filtetrA</filter-name>
        <filter-class>com.chs.filter.FilterTest</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>filtetrA</filter-name>
        <url-pattern>/filterTest</url-pattern>  <!-- /filterTest 精确匹配-->
</filter-mapping>

说明

  • filter-mapping标签中定义了过滤器对那些资源进行过滤;
  • 子标签url-pattern通过映射路径确定过滤范围
    • /servletA 精确匹配,表示对servletA资源的请求进行过滤;
    • *.html 表示对以.action结尾的路径进行过滤;
    • /* 表示对所有资源进行过滤;
    • 一个filter-mapping下可以配置多个url-pattern;
  • 子标签servlet-name通过servlet别名确定对那些servlet进行过滤
    • 使用该标签确定目标资源的前提是servlet已经起了别名;
    • 一个filter-mapping下可以定义多个servlet-name;
    • 一个filter-mapping下,servlet-name和url-pattern子标签可以同时存在;

二、注解方式映射配置过滤器

@WebFilter注解

@WebFilter(filterName="默认也是包名.类名",value={"/*"})

@WebFilter("/filterTest")
@WebFilter(
        filterName = "loggingFilter",
        initParams = {@WebInitParam(name="dateTimePattern",value="yyyy-MM-dd HH:mm:ss")},
        urlPatterns = {"/servletA","*.html"},
        servletNames = {"servletBName"}
)
public class FilterTest extends HttpFilter {
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("过滤器");
        chain.doFilter(request,response);  //放行
    }
}

1682496991032

过滤器生命周期

过滤器作为web项目的组件之一,和Servlet的生命周期类似,略有不同,没有servlet的load-on-startup的配置,默认就是系统启动立刻构造。

阶段 对应方法 执行时机 执行次数
创建对象 构造器 web应用启动时 1
初始化方法 void init(FilterConfig filterConfig) 构造完毕 1
过滤请求 void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 每次请求 多次
销毁 default void destroy()

过滤器链的使用

一个web项目中可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,称为过滤器链。

  • 过滤器链中的过滤器的顺序由filter-mapping顺序决定;
  • 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的;
  • 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低;
  • 写了xml和注解都生效,那么先去解析xml,再去解析注解,因此xml当中配置的优先执行
  • 都写成注解,那么按照过滤器类的加载顺序[类的名字]去执行,顺序是字母的万国码顺序 例如a就比b优先执行

图解过滤器链:

1682556566084

监听器

监听器:专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象;

  • 监听器是GOF设计模式中,观察者模式的典型案例;
  • 监听器使用的感受类似JS中的事件,被观察的对象发生某些情况时,自动触发代码的执行;
  • 监听器并不监听web项目中的所有组件,仅仅是对三大域对象做相关的事件监听;

监听器的分类:

  • web中定义八个监听器接口作为监听器的规范,这八个接口按照不同的标准可以形成不同的分类;

  • 按监听的对象划分:

    • application域监听器 ServletContextListener ServletContextAttributeListener ;
    • session域监听器 HttpSessionListener HttpSessionAttributeListener HttpSessionBindingListener HttpSessionActivationListener ;
    • request域监听器 ServletRequestListener ServletRequestAttributeListener ;
  • 按监听的事件分:

    • 域对象的创建和销毁监听器 ServletContextListener HttpSessionListener ServletRequestListener ;
    • 域对象数据增删改事件监听器 ServletContextAttributeListener HttpSessionAttributeListener ServletRequestAttributeListener ;
    • 其他监听器 HttpSessionBindingListener HttpSessionActivationListener ;

监听器的六个主要接口

application域监听器

ServletContextListener 监听ServletContext对象的创建与销毁。

方法名 作用
contextInitialized(ServletContextEvent sce) ServletContext创建时调用
contextDestroyed(ServletContextEvent sce) ServletContext销毁时调用
  • ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。

ServletContextAttributeListener 监听ServletContext中属性的添加、移除和修改。

方法名 作用
attributeAdded(ServletContextAttributeEvent scab) 向ServletContext中添加属性时调用
attributeRemoved(ServletContextAttributeEvent scab) 从ServletContext中移除属性时调用
attributeReplaced(ServletContextAttributeEvent scab) 当ServletContext中的属性被修改时调用
  • ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下:
方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getServletContext() 获取ServletContext对象

测试代码

  • 定义监听器:
@WebListener
public class ApplicationListener implements ServletContextListener , ServletContextAttributeListener {
    // 监听初始化
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext application = sce.getServletContext();
        System.out.println("application"+application.hashCode()+" initialized");
    }
    // 监听销毁
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext application = sce.getServletContext();
        System.out.println("application"+application.hashCode()+" destroyed");
    }

    // 监听数据增加
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        String name = scae.getName();
        Object value = scae.getValue();
        ServletContext application = scae.getServletContext();
        System.out.println("application"+application.hashCode()+" add:"+name+"="+value);
    }

    // 监听数据移除
    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        String name = scae.getName();
        Object value = scae.getValue();
        ServletContext application = scae.getServletContext();
        System.out.println("application"+application.hashCode()+" remove:"+name+"="+value);
    }
    // 监听数据修改
    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        String name = scae.getName();
        Object value = scae.getValue();
        ServletContext application = scae.getServletContext();
        Object newValue = application.getAttribute(name);
        System.out.println("application"+application.hashCode()+" change:"+name+"="+value+" to "+newValue);
    }
}
  • 定义触发监听器的代码:
// ServletA用于向application域中放入数据
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 向application域中放入数据
        ServletContext application = this.getServletContext();
        application.setAttribute("k1","v1");
        application.setAttribute("k2","v2");
    }
}
// ServletB用于向application域中修改和移除数据
@WebServlet(urlPatterns = "/servletB", name = "servletBName")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext appliation  = getServletContext();
        //  修改application域中的数据
        appliation.setAttribute("k1","value1");
        //  删除application域中的数据
        appliation.removeAttribute("k2");
    }
}

session域监听器

HttpSessionListener 监听HttpSession对象的创建与销毁:

方法名 作用
sessionCreated(HttpSessionEvent hse) HttpSession对象创建时调用
sessionDestroyed(HttpSessionEvent hse) HttpSession对象销毁时调用
  • HttpSessionEvent对象代表从HttpSession对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpSession对象。

HttpSessionAttributeListener 监听HttpSession中属性的添加、移除和修改:

方法名 作用
attributeAdded(HttpSessionBindingEvent se) 向HttpSession中添加属性时调用
attributeRemoved(HttpSessionBindingEvent se) 从HttpSession中移除属性时调用
attributeReplaced(HttpSessionBindingEvent se) 当HttpSession中的属性被修改时调用
  • HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:
方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getSession() 获取触发事件的HttpSession对象

测试代码:

  • 定义监听器:
@WebListener
public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener {
    // 监听session创建
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("session"+session.hashCode()+" created");
    }
    // 监听session销毁
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("session"+session.hashCode()+" destroyed");
    }
    // 监听数据增加
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object value = se.getValue();
        HttpSession session = se.getSession();
        System.out.println("session"+session.hashCode()+" add:"+name+"="+value);
    }
    // 监听数据移除
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object value = se.getValue();
        HttpSession session = se.getSession();
        System.out.println("session"+session.hashCode()+" remove:"+name+"="+value);
    }
    // 监听数据修改
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object value = se.getValue();
        HttpSession session = se.getSession();
        Object newValue = session.getAttribute(name);
        System.out.println("session"+session.hashCode()+" change:"+name+"="+value+" to "+newValue);
    }
}
  • 定义触发监听器的代码:
// servletA用于创建session并向session中放数据
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建session,并向session中放入数据
        HttpSession session = req.getSession();

        session.setAttribute("k1","v1");
        session.setAttribute("k2","v2");
    }
}
// servletB用于修改删除session中的数据并手动让session不可用
@WebServlet(urlPatterns = "/servletB", name = "servletBName")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        //  修改session域中的数据
        session.setAttribute("k1","value1");
        //  删除session域中的数据
        session.removeAttribute("k2");
        // 手动让session不可用
        session.invalidate();
    }
}

request域监听器

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

方法名 作用
requestInitialized(ServletRequestEvent sre) ServletRequest对象创建时调用
requestDestroyed(ServletRequestEvent sre) ServletRequest对象销毁时调用
  • ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象。

ServletRequestAttributeListener 监听ServletRequest中属性的添加、移除和修改:

方法名 作用
attributeAdded(ServletRequestAttributeEvent srae) 向ServletRequest中添加属性时调用
attributeRemoved(ServletRequestAttributeEvent srae) 从ServletRequest中移除属性时调用
attributeReplaced(ServletRequestAttributeEvent srae) 当ServletRequest中的属性被修改时调用
  • ServletRequestAttributeEvent对象代表属性变化事件,它包含的方法如下:
方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getServletRequest () 获取触发事件的ServletRequest对象
  • 定义监听器:
@WebListener
public class RequestListener implements ServletRequestListener , ServletRequestAttributeListener {
    // 监听初始化
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        ServletRequest request = sre.getServletRequest();
        System.out.println("request"+request.hashCode()+" initialized");
    }
    // 监听销毁
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        ServletRequest request = sre.getServletRequest();
        System.out.println("request"+request.hashCode()+" destoryed");
    }
    // 监听数据增加
    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        String name = srae.getName();
        Object value = srae.getValue();
        ServletRequest request = srae.getServletRequest();
        System.out.println("request"+request.hashCode()+" add:"+name+"="+value);
    }
    //  监听数据移除
    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        String name = srae.getName();
        Object value = srae.getValue();
        ServletRequest request = srae.getServletRequest();
        System.out.println("request"+request.hashCode()+" remove:"+name+"="+value);
    }
    // 监听数据修改
    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        String name = srae.getName();
        Object value = srae.getValue();
        ServletRequest request = srae.getServletRequest();
        Object newValue = request.getAttribute(name);
        System.out.println("request"+request.hashCode()+" change:"+name+"="+value+" to "+newValue);
    }
}
  • 定义触发监听器的代码:
//  servletA向请求域中放数据
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 向request中增加数据
        req.setAttribute("k1","v1");
        req.setAttribute("k2","v2");
        // 请求转发
        req.getRequestDispatcher("servletB").forward(req,resp);
    }
}
// servletB修改删除域中的数据
@WebServlet(urlPatterns = "/servletB", name = "servletBName")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //  修改request域中的数据
        req.setAttribute("k1","value1");
        //  删除session域中的数据
        req.removeAttribute("k2");
    }
}

session域的两个特殊监听器

session绑定监听器

HttpSessionBindingListener 监听当前监听器对象在Session域中的增加与移除:

方法名 作用
valueBound(HttpSessionBindingEvent event) 该类的实例被放到Session域中时调用
valueUnbound(HttpSessionBindingEvent event) 该类的实例从Session中移除时调用
  • HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:
方法名 作用
getName() 获取当前事件涉及的属性名
getValue() 获取当前事件涉及的属性值
getSession() 获取触发事件的HttpSession对象

测试代码:

  • 定义监听器:
public class MySessionBindingListener  implements HttpSessionBindingListener {
    //  监听绑定
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        String name = event.getName();
        System.out.println("MySessionBindingListener"+this.hashCode()+" binding into session"+session.hashCode()+" with name "+name);
    }
    // 监听解除绑定
    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        String name = event.getName();
        System.out.println("MySessionBindingListener"+this.hashCode()+" unbond outof session"+session.hashCode()+" with name "+name);
    }
}
  • 定义触发监听器的代码:
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        // 绑定监听器
        session.setAttribute("bindingListener",new MySessionBindingListener());
        // 解除绑定监听器
        session.removeAttribute("bindingListener");
    }
}

钝化活化监听器

HttpSessionActivationListener 监听某个对象在Session中的序列化与反序列化:

方法名 作用
sessionWillPassivate(HttpSessionEvent se) 该类实例和Session一起钝化到硬盘时调用
sessionDidActivate(HttpSessionEvent se) 该类实例和Session一起活化到内存时调用
  • HttpSessionEvent对象代表事件对象,通过getSession()方法获取事件涉及的HttpSession对象。

什么是钝化活化:

  • session对象在服务端是以对象的形式存储于内存的,session过多,服务器的内存也是吃不消的;
  • 而且一旦服务器发生重启,所有的session对象都将被清除,也就意味着session中存储的不同客户端的登录状态丢失;
  • 为了分摊内存 压力并且为了保证session重启不丢失,我们可以设置将session进行钝化处理;
  • 在关闭服务器前或者到达了设定时间时,对session进行序列化到磁盘,这种情况叫做session的钝化;
  • 在服务器启动后或者再次获取某个session时,将磁盘上的session进行反序列化到内存,这种情况叫做session的活化;

如何配置钝化活化?

  • 在web目录下,添加 META-INF下创建Context.xml。

1682565824241

  • 文件中配置钝化。
<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
        <Store className="org.apache.catalina.session.FileStore" directory="d:\mysession"></Store>
    </Manager>
</Context>
  • 请求servletA,获得session,并存入数据,然后重启服务器:
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        // 添加数据
        session.setAttribute("k1","v1");
    }
}
  • 请求servletB获取session,获取重启前存入的数据:
@WebServlet(urlPatterns = "/servletB", name = "servletBName")
public class ServletB extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        Object v1 = session.getAttribute("k1");
        System.out.println(v1);
    }
}

如何监听钝化活化?

  • 定义监听器:
package com.atguigu.listeners;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionActivationListener;
import jakarta.servlet.http.HttpSessionEvent;
import java.io.Serializable;
public class ActivationListener  implements HttpSessionActivationListener, Serializable {
    //  监听钝化
    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("session with JSESSIONID "+ session.getId()+" will passivate");
    }
    //  监听活化
    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("session with JSESSIONID "+ session.getId()+" did activate");
    }
}
  • 定义触发监听器的代码:
@WebServlet(urlPatterns = "/servletA",name = "servletAName")
public class ServletA extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        // 添加数据
        session.setAttribute("k1","v1");
        
        
        // 添加钝化活化监听器
        session.setAttribute("activationListener",new ActivationListener());
    }
}
posted @ 2024-08-26 20:59  CH_song  阅读(7)  评论(0编辑  收藏  举报