Tomcat+Servlet
Tomcat+Servlet
XML

XML是EXtensible Markup Language的缩写,翻译过来就是可扩展标记语言。所以很明显,XML和HTML一样都是标记语言,也就是说它们的基本语法都是标签。
-
可扩展:三个字表面上的意思是XML允许自定义格式。但这不代表你可以随便写;
-
在XML基本语法规范的基础上,你使用的那些第三方应用程序、框架会通过XML约束的方式强制规定配置文件中可以写什么和怎么写;
-
XML基本语法这个知识点的定位是:
我们不需要从零开始,从头到尾的一行一行编写XML文档,而是在第三方应用程序、框架已提供的配置文件的基础上修改。
要改成什么样取决于你的需求,而怎么改取决XML基本语法和具体的XML约束;
常见配置文件类型
- properties: 例如druid连接池就是使用properties文件作为配置文件;
- XML,: 例如Tomcat就是使用XML文件作为配置文件;
- YAML/YML: 例如SpringBoot就是使用YAML作为配置文件;
- json: 通常用来做文件传输,也可以用来做前端或者移动端的配置文件;
- ... ...
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的使用步骤
- 导入jar包 dom4j.jar
- 创建解析器对象(SAXReader)
- 解析xml 获得Document对象
- 获取根节点RootElement
- 获取根节点下的子节点
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地址供网络中的其他人访问;

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

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,
因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器
。
安装
版本:
- 版本:企业用的比较广泛的是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/;
-
安装版:需要安装,一般不考虑使用;
-
解压版:直接解压缩使用,我们使用的版本;
安装:
- 正确安装JDK并配置JAVA_HOME。
- 解压Tomcat到非中文无空格目录。
- 点击bin/startup.bat启动。
- 打开浏览器输入 http://localhost:8080访问测试。
-
直接关闭窗口或者运行 bin/shutdown.bat关闭tomcat。
-
处理dos窗口日志中文乱码问题:修改conf/logging.properties,ConsoleHandler.encoding的UTF-8修改为GBK。
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项目标准结构如下:
- 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的组成部分和项目中资源的对应关系:
WEB项目部署的方式
方式1 直接将编译好的项目放在webapps目录下 ,然后在浏览器中通过更改路径访问项目。
方式2 将编译好的项目打成war包放在webapps目录,tomcat启动后会自动解压war包(其实和第一种一样,后面通过maven完成)。
方式3 可以将项目放在非webapps的其他目录下,在Tomcat中通过配置文件指向app的实际磁盘路径。
- 在磁盘的自定义目录上准备一个app

- 在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:
IDEA创建web工程
推荐先创建一个空项目,既workspace,这样可以在一个空项目下同时存在多个modules,不用后续来回切换之前的项目,当然也可以忽略此步直接创建项目:
检查项目的SDK,语法版本,以及项目编译后的输出目录:
先创建一个普通的Java项目:
创建完毕后,为项目添加Tomcat依赖:
添加 web-modules(web目录)
artifacts中添加本web目录
或者--》选择modules,在help中搜索support 添加 framework support:
选择Web Application 注意Version,勾选 Create web.xml:

删除项目步骤
1、先移除项目
右击项目 -- Remove Module(项目变灰) -- 右击删除
2、删除wer包
file -- Project Structure -- Project settings -- Artifacts 选择- 移除要删除的wer包
处理配置文件:
- 在工程下创建resources目录,专门用于存放配置文件(都放在src下也行,单独存放可以尽量避免文件集中存放造成的混乱);
- 标记目录为资源目录,不标记的话则该目录不参与编译;
- 标记完成后,显示效果如下:
处理依赖jar包问题:
- 在WEB-INF下创建lib目录。
- 必须在WEB-INF下,且目录名必须叫lib!!!
- 复制jar文件进入lib目录。
- 将lib目录添加为当前项目的依赖,后续可以用maven统一解决。

- 环境级别推荐选择module 级别,降低对其他项目的影响,name可以空着不写。
IDEA部署-运行web项目
检查idea是否识别modules为web项目,并且存在将项目构建成发布结构的配置:
- 就是检查工程目录下,web目录有没有特殊的识别标记。
- 以及artifacts下,有没有对应 _war_exploded,如果没有,就点击+号添加。
点击向下箭头,出现 Edit Configurations选项:
出现运行配置界面:
点击+号,添加本地Tomcat服务器:

因为IDEA 只关联了一个Tomcat,红色部分就只有一个Tomcat可选:
选择Deployment,通过+添加要部署到Tomcat中的artifact:

Application context中是默认的项目上下文路径,也就是url中需要输入的路径,这里可以自己定义,可以和工程名称不一样,也可以不写,但是要保留/,这里暂时就用默认的:
项目上下文路径:在idea中我们可以自定义项目的访问路径,也叫上下文路径 contextPath
点击apply 应用后,回到Server部分。After Launch是配置启动成功后,是否默认自动打开浏览器并输入URL中的地址,HTTP port是HTTP连接器目前占用的端口号:

点击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配置文件的形式实现项目部署的;
最终idea中web项目源码目录结构
resources: 存放项目的配置文件的 创建目录需要 mark as
src:java的源码
web:前端的资源文件
WEB-INF目录 服务器保护的资源文件,只能是程序内部才能访问,不能浏览器直接去请求访问
lib目录 放web项目的第三方jar包位置
web.xml javaweb的核心配置文件
HTTP协议
HTTP简介


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
- 请求头信息(请求头);
- 空行;
- 请求体;
响应报文
响应报文格式:
- 响应首行(响应行); 协议/版本 状态码 状态码描述
- 响应头信息(响应头);
- 空行;
- 响应体;
响应状态码:响应码对浏览器来说很重要,它告诉浏览器响应的结果。比较有代表性的响应码如下:
- 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之间的对应关系:

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.测试
方式二
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.测试
总结注意:
1.前端请求路径要和xml文件中的
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对象,初始化 进行服务
6 xml的配置可以让我们的项目启动就初始化创建,而不是请求的时候才去创建配置的数字代表顺序启动,因为后期可能有多个servlet
生命周期总结
- 通过生命周期测试发现Servlet对象在容器中是单例的;
- 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程, 但是对象只有一个;
- 多个线程可能会使用相同的Servlet对象,所以在Servlet中,我们不要轻易定义一些需要经常修改的成员变量;
- load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复;
- Tomcat容器中,已经定义了一些随系统启动实例化的Servlet,自定义的Servlet的load-on-startup尽量不要占用数字1-5;
- 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 void init(ServletConfig config) throws ServletException;
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的方法。
- private transient ServletConfig config;
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
继承关系图解:
- 自定义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作为属性;

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文件中写

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代表客户端发来的请求,请求中的所有信息都可以通过该对象获得;
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 |
获取所有的请求头名字。 |
String getContentType(); | 获取content-type请求头。 |
- 获得请求参数相关:
API | 功能解释 |
---|---|
String getParameter(String parameterName); | 根据请求参数名获取请求单个参数值。 |
String[] getParameterValues(String parameterName); | 根据请求参数名获取请求多个参数值数组。 |
Enumeration |
获取所有请求参数名。 |
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代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息;
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实现;
请求转发
请求转发特点(背诵):
- 请求转发通过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
响应重定向
响应重定向运行逻辑图:

响应重定向特点(背诵):
- 响应重定向通过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乱码和路径问题总结
乱码问题
乱码问题产生的根本原因是什么?
- 数据的编码和解码使用的不是同一个字符集
- 使用了不支持某个语言文字的字符集
各个字符集的兼容性:

- 由上图得知,上述字符集都兼容了ASCII;
- ASCII中有什么? 英文字母和一些通常使用的符号,所以这些东西无论使用什么字符集都不会乱码;
HTML乱码问题
设置项目文件的字符集要使用一个支持中文的字符集。
- 查看当前文件的字符集。

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

当前视图文件的字符集通过
<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文件的字符集和编译时使用的字符集一致。

请求乱码问题
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" />
。
- GET方式提交时,会对数据进行URL编码处理 ,是将GBK 转码为 "百分号码"。

- Tomcat10.1.7 默认使用UTF-8对URI进行解析,造成前后端使用的字符集不一致,出现乱码。
GET请求方式乱码解决:
- 方式1 :设置GET方式提交的编码和Tomcat10.1.7的URI默认解析编码一致即可 (推荐)。

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


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

- 请求体中,将GBK数据进行 URL编码。
- 后端默认使用UTF-8解析请求体,出现字符集不一致,导致乱码。

POST请求方式乱码解决:
- 方式1 : 请求时,使用UTF-8字符集提交请求体 (推荐)。


- 方式2 : 后端在获取参数前,设置解析请求体使用的字符集和请求发送时使用的字符集一致 (此时不推荐)。
响应乱码问题
响应乱码分析:
- 在Tomcat10.1.7中,向响应体中放入的数据默认使用了工程编码 UTF-8。
- 浏览器在接收响应信息时,使用了不同的字符集或者是不支持中文的字符集就会出现乱码。
响应乱码演示:
- 服务端通过response对象向响应体添加数据。

- 浏览器接收数据解析乱码。

响应乱码解决:
-
方式1 :手动设定浏览器对本次响应体解析时使用的字符集(不推荐)。
- edge和 chrome浏览器没有提供直接的比较方便的入口,不方便;
-
方式2:后端通过设置响应体的字符集和浏览器解析响应体的默认字符集一致(此时不推荐)。

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

路径问题
相对路径和绝对路径:
-
相对路径:
- 相对路径的规则是以当前资源所在的路径为出发点去寻找目标资源;
- 相对路径不以 / 开头;
- 在file协议下,使用的是磁盘路径;
- 在http协议下,使用的是url路径;
- 相对路径中可以使用 ./表示当前资源所在路径,可以省略不写;
- 相对路径中可以使用../表示当前资源所在路径的上一层路径,需要时要手动添加;
-
绝对路径:
- 绝对路径的规则是: 使用以一个固定的路径做出出发点去寻找目标资源,和当前资源所在的路径没有关系;
- 绝对路径要以/ 开头;
- 绝对路径的写法中,不以当前资源的所在路径为出发点,所以不会出现 ./ 和../开头;
- 不同的项目和不同的协议下,绝对路径的基础位置可能不同,要通过测试确定;
- 绝对路径的好处就是,无论当前资源位置在哪,寻找目标资源路径的写法都一致;
-
应用场景:
- 前端代码中,href、src、action 等属性;
- 请求转发和重定向中的路径;
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,将Cookie放入响应对象中,Tomcat容器将Cookie转化为set-cookie响应头,响应给客户端;
- 客户端在收到Cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie;
- Cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐;
- 由于Cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据;
应用场景举例:
- 记录用户名;
- 保存电影播放进度;
Cookie的使用
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的有效期是一次会话范围内,我们可以通过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就可以存储一些专门针对某个客户端的信息了;
原理图如下:
应用场景:
-
记录用户的登录状态:
用户登录后,将用户的账号等敏感信息存入session;
-
记录用户操作的历史:
例如记录用户的访问痕迹,用户的购物车信息等临时性的信息;
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方法的处理逻辑:
HttpSession时效性
默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在Tomcat/conf/web.xml配置为30分钟。
也可以通过HttpSession的API 对最大闲置时间进行设定。
// 设置最大闲置时间
session.setMaxInactiveInterval(5); //是两次使用session的间隔超过5秒就过期
也可以直接让session失效。
// 直接让session失效
session.invalidate(); //[用于退出登录]
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); //放行
}
}
过滤器生命周期
过滤器作为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优先执行
图解过滤器链:

监听器
监听器:专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象;
- 监听器是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。
- 文件中配置钝化。
<?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());
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?