系统架构概述
一、系统架构的形式
系统架构主要包括以下两种形式:
- C/S 架构(Client/Server)
- B/S 架构(Browser/Server)
二、C/S 架构详解
1. 什么是 C/S 架构?
C/S 架构即客户端 / 服务器架构,是一种典型的两层结构。
2. 常见的 C/S 架构系统有哪些?
- 微信
3. C/S 架构的特点
- 需要安装特定的客户端软件。
4. C/S 架构的优缺点
✅ 优点:
- 速度快:客户端本地存储大量数据,仅需少量网络传输。
- 体验好:界面响应快,交互流畅。
- 界面酷炫:可使用专业开发语言实现丰富界面。
- 服务器压力小:大部分处理在客户端完成。
- 安全性高:数据分散在多个客户端,即使服务器故障也不易丢失全部数据。
❌ 缺点:
- 升级维护成本高:每个客户端都需要单独更新。
- 部署复杂:部分软件安装过程繁琐。
三、B/S 架构详解
1. 什么是 B/S 架构?
B/S 架构即浏览器 / 服务器架构,用户通过浏览器访问服务器资源。
2. 常见的 B/S 架构系统示例
- 百度:http://www.baidu.com
- 京东:http://www.jd.com
- 网易邮箱:http://www.126.com
3. B/S 是否属于 C/S 的一种?
是的,B/S 是一种特殊的 C/S 架构。其中,“客户端”是一个通用浏览器。
4. B/S 架构的优缺点
✅ 优点:
- 升级维护方便:只需更新服务器端即可。
- 无需安装客户端:用户只需打开浏览器访问网址即可操作。
❌ 缺点:
- 速度较慢:所有数据都需从服务器获取,依赖网络带宽。
- 用户体验较差:受限于 HTML/CSS/JS,界面表现力有限。
- 安全性较低:所有数据集中在服务器,一旦服务器出问题,数据可能全部丢失。
四、C/S 与 B/S 架构哪个更好?
这个问题没有绝对答案,取决于具体业务场景:
场景 | 推荐架构 |
---|---|
娱乐类软件(如社交、游戏) | C/S 架构 |
公司内部管理系统(如OA、ERP) | B/S 架构 |
原因说明:
- 公司内部系统要求:
- 升级维护成本低
- 不需要特别炫酷的界面
- 主要用于数据维护和流程管理
五、开发 B/S 系统所需技术
开发一个 B/S 系统,其实就是开发一个 Web 系统,通常包括以下几个方面:
1. Web 前端技术(运行在浏览器中)
- HTML
- CSS
- JavaScript
2. Web 后端技术(运行在服务器上)
可以使用多种语言开发,例如:
- Java(JavaWEB 开发,核心规范为 Servlet)
- C
- C++
- Python
- PHP
- …
六、JavaEE 是什么?
Java 分为三大方向:
1. JavaSE(Java 标准版)
- 提供基础类库和 API。
- 是学习 JavaEE 和 JavaME 的基础。
2. JavaEE(Java 企业版)
- 专为企业级应用设计的一套类库。
- 支持 Web 系统开发,包含 13 种规范,Servlet 是其中之一。
- 是目前 Java 最热门的方向之一。
3. JavaME(Java 微型版)
- 适用于嵌入式设备开发,如:
- 机顶盒
- 吸尘器
- 电冰箱
- 电饭煲等小型电子设备
总结
架构类型 | 特点 | 适用场景 |
---|---|---|
C/S | 快速、体验好、界面炫、安全 | 娱乐类、客户端需求高的应用 |
B/S | 易维护、无需安装、跨平台 | 企业内部系统、Web 应用 |
提示:选择架构时应结合项目需求、团队能力及长期维护等因素综合考虑。
B/S 结构的系统通信原理(不涉及 Java 小程序)
一、WEB 系统的访问过程
- 打开浏览器
- 找到地址栏
- 输入一个合法的网址
- 按下回车键
- 浏览器展示响应结果
二、关于域名
- 示例网址:
https://www.baidu.com/
www.baidu.com
是一个域名
- 在浏览器中输入域名后:
- 域名解析器会将域名解析为具体的 IP 地址和端口号
- 解析示例:
http://110.242.68.3:80/index.html
三、IP 地址的作用
- IP 地址是计算机在网络中的“身份证号”
- 在同一个网络中,IP 地址具有唯一性
- 要实现两台计算机之间的通信,必须知道对方的 IP 地址
四、端口号的作用
- 一个端口代表一个软件或服务
- 每个软件启动后都会占用一个端口号
- 同一台计算机上,端口号具有唯一性
五、WEB 系统的通信原理与步骤
- 用户在浏览器中输入网址(URL)
- 域名解析器进行解析,得到完整的 URL(如
http://110.242.68.3:80/index.html
) - 浏览器根据 IP 地址在网络上寻找对应的主机(如
110.242.68.3
) - 定位该主机上的服务器软件(通过端口号 80)
- 服务器得知用户请求的资源名称(如
index.html
) - 服务器查找并读取该资源文件
- 服务器将文件内容发送给浏览器(响应数据)
- 浏览器接收 HTML/CSS/JS 数据
- 浏览器渲染页面,执行代码,最终展示网页效果
六、什么是 URL?
- 定义:统一资源定位符(Uniform Resource Locator)
- 示例:
http://www.baidu.com
七、请求与响应的概念
类型 | 数据流向 | 英文术语 | 中文解释 |
---|---|---|---|
请求 | 浏览器 → 服务器 | request | Browser 向 Server 发送数据 |
响应 | 服务器 → 浏览器 | response | Server 向 Browser 返回数据 |
关于Web服务器软件
常见的Web服务器软件
以下是一些常见的Web服务器软件(均为已开发完成的成熟产品):
- Tomcat:轻量级Web服务器
- Jetty:嵌入式Web服务器
- JBoss:应用服务器
- WebLogic:企业级应用服务器
- WebSphere:IBM出品的企业级应用服务器
应用服务器与Web服务器的关系
- 应用服务器实现了JavaEE平台中的所有13个规范。
- Web服务器仅实现了Servlet和JSP两个核心规范。
- 由此可见,应用服务器包含Web服务器。
- 例如,JBoss中就内嵌了一个Tomcat服务器。
Tomcat概述
- 官网地址:
- Apache官网:https://www.apache.org
- Tomcat官网:https://tomcat.apache.org
- 特点:
- 开源免费
- 轻量级Web服务器
- 使用Java语言编写
- 运行需要JRE环境支持
- 别名:Catalina
- 名字来源于美国的一个风景优美的岛屿,寓意Tomcat服务器小巧、高效。
- Logo:一只公猫,象征其轻巧、快速的特性。
Tomcat配置Java运行环境
Tomcat是基于Java编写的,因此运行前必须安装JDK并配置Java运行环境变量:
|
|
💡 思考问题:如果没有配置
JAVA_HOME
是否可行?
虽然在某些情况下可以运行,但为了稳定性和兼容性,建议始终正确配置。
Tomcat安装方式
Tomcat为绿色版本,无需复杂安装,只需解压ZIP包即可使用。
推荐安装路径(示例):
C:\dev\tomcat
:将所有开发工具集中管理,便于维护。
启动Tomcat
进入bin
目录,执行以下命令启动Tomcat:
- Windows系统:
startup.bat
- Linux系统:
startup.sh
批处理文件说明
.bat
文件:Windows下的批处理脚本,可批量执行DOS命令。.sh
文件:Linux下的Shell脚本,用于执行Shell命令。- Tomcat提供了跨平台的支持,适配多种操作系统。
启动流程解析
startup.bat
实际调用了catalina.bat
。- 在
catalina.bat
中定义了主类:1
MAINCLASS=org.apache.catalina.startup.Bootstrap
- 该类中包含
main()
方法,即Tomcat启动入口。
环境变量要求
启动Tomcat需要配置以下环境变量:
|
|
Tomcat目录结构详解
目录名 | 用途说明 |
---|---|
bin/ |
存放启动、关闭等命令脚本(如 startup.bat , shutdown.sh ) |
conf/ |
配置文件目录,如 server.xml 可修改端口号等设置 |
lib/ |
核心类库目录,存放Tomcat运行所需的JAR文件 |
logs/ |
日志文件目录,记录服务器运行日志 |
temp/ |
临时文件存储目录 |
webapps/ |
Web应用部署目录,放置 .war 包或项目文件夹 |
work/ |
JSP编译后生成的Java文件和 .class 文件 |
Tomcat启动与关闭
- 启动:执行
startup.bat
或startup.sh
- 关闭:执行
shutdown.bat
(建议重命名为stop.bat
,避免与系统关机命令冲突)
测试Tomcat是否启动成功
打开浏览器访问以下任一地址:
|
|
如果看到Tomcat欢迎页面,则表示服务器已成功启动。
实现一个最基本的 Web 应用(该应用中不包含 Java 小程序)
一、准备工作
在开始之前,请确保你已经安装并配置好了 Tomcat 服务器,并设置了 CATALINA_HOME
环境变量。
二、步骤详解
第一步:定位到 webapps
目录
-
找到 Tomcat 的
webapps
目录:1
CATALINA_HOME\webapps
⚠️ 注意:所有 Web 应用都必须放置在该目录下,这是 Tomcat 的规定。否则服务器将无法识别你的应用。
第二步:创建一个新的 Web 应用目录
- 在
webapps
目录下新建一个子目录,命名为oa
。
📁 说明:目录名
oa
即为你的 Web 应用名称。
第三步:添加资源文件
- 在
oa
目录下创建资源文件,例如index.html
。 - 编写 HTML 文件内容以展示页面信息。
第四步:启动 Tomcat 服务器
- 启动 Tomcat,等待服务器完成部署。
第五步:访问你的 Web 应用
- 打开浏览器,在地址栏输入以下 URL:
|
|
三、关于 URL 访问的思考
浏览器直接输入 URL 与超链接的区别?
当你在浏览器地址栏输入一个 URL 并回车时,其本质与点击一个超链接是一样的。因此,我们可以在网页中使用超链接来实现相同的效果。
示例代码:
|
|
✅ 提示:前端路径建议统一使用绝对路径,格式为
/项目名/资源路径
,无需添加协议和域名。
四、静态资源与动态资源的区别
静态资源示例:
|
|
- 当前这个页面是静态 HTML 文件,数据是“硬编码”写死在文件中的。
- 这类资源被称为 静态资源。
动态资源的需求:
- 如果希望页面上的数据能够根据数据库中的内容变化而自动更新,则需要引入动态网页技术。
- 动态网页不是指有动画效果的页面,而是指页面的数据是动态生成的。
如何实现动态网页?
- 使用 JDBC 技术连接数据库。
- 编写 Java 程序从数据库中读取数据。
- 根据查询结果动态生成 HTML 页面内容。
💡 总结:动态网页的关键在于“数据可变”,通常依赖于后端逻辑和数据库交互。
动态 Web 应用中请求与响应过程涉及的角色与协议
一、参与的角色
在一个典型的 B/S(Browser/Server)架构系统中,一个完整的请求与响应过程会涉及到多个角色的协同工作。具体包括以下几个开发团队及其所代表的技术角色:
1. 浏览器软件开发团队
- 负责开发浏览器程序。
- 常见浏览器包括:
- Google Chrome
- Mozilla Firefox
- Microsoft Edge / Internet Explorer
- Safari 等
2. Web Server 开发团队
- 提供 Web 容器服务,负责接收 HTTP 请求并处理静态资源或转发给 Web 应用处理。
- 常见 Web Server 包括:
- Apache Tomcat
- Jetty
- Oracle WebLogic
- JBoss
- IBM WebSphere 等
3. DB Server 开发团队
- 提供数据库管理系统,用于存储和管理数据。
- 常见数据库系统包括:
- Oracle
- MySQL
- PostgreSQL
- SQL Server 等
4. Web 应用开发团队(Java Web 开发者)
- 编写实际业务逻辑的 Web 应用程序(WebApp),部署在 Web Server 上运行。
二、角色之间的规范与协议
为了保证各个角色之间能够有效协作,需要遵循一系列标准和协议。
1. Web App 开发团队 与 Web Server 开发团队之间
- 规范:Servlet 规范(属于 JavaEE 标准的一部分)
- 作用:
- 实现 Web Server 与 Web 应用之间的解耦合。
- 确保 Web 应用可以在不同的 Web Server 中兼容运行。
Servlet 规范包含哪些内容?
- 规定了哪些接口和类必须存在。
- 规定了 Web 应用应包含的配置文件:
- 配置文件名称(如
web.xml
) - 存放路径(如
/WEB-INF/
) - 文件内容格式
- 配置文件名称(如
- 规定了 Web 应用的标准目录结构。
- 其他与 Web 应用生命周期、请求处理相关的规范。
2. 浏览器(Browser) 与 Web Server 之间
- 协议:HTTP 协议(HyperText Transfer Protocol)
- 作用:
- 浏览器向服务器发起请求。
- 服务器返回 HTML 页面或其他资源。
- 是浏览器与服务器之间通信的基础协议。
3. Web App 开发团队 与 DB Server 开发团队之间
- 规范:JDBC(Java Database Connectivity)
- 作用:
- 提供统一的 API 接口,使 Java 程序可以访问各种数据库。
- 实现数据库操作的标准化和可移植性。
三、Servlet 规范详解
1. 什么是 Servlet 规范?
- 是 JavaEE 标准中定义的一套编程规范。
- 只要 Web 应用遵循该规范编写,就可以部署在任何支持该规范的 Web Server 上运行。
2. Servlet 规范的主要内容
内容类型 | 描述 |
---|---|
接口与类 | 定义了 Servlet , HttpServlet , ServletRequest , ServletResponse 等核心接口和类 |
配置文件 | 指定 web.xml 的格式、位置和内容 |
目录结构 | 明确 Web 应用的标准目录结构(如 WEB-INF , classes , lib 等) |
生命周期 | 规定 Servlet 的初始化、服务、销毁等生命周期方法 |
请求处理 | 规定如何处理 HTTP 请求(GET、POST 等) |
开发一个带有 Servlet 的 Web 应用(重点)
一、开发步骤
第一步:创建 Web 应用的根目录
在 webapps
目录下新建一个项目文件夹,例如:
crm
(客户关系管理系统)bank
(银行系统)oa
(办公自动化系统)
注意:该目录即为 Web 应用的“根目录”。
第二步:创建 WEB-INF
目录
在项目根目录下新建一个名为 WEB-INF
的子目录。
注意:
- 名称必须全大写,不能更改。
- 该目录是 Servlet 规范中规定的标准目录。
第三步:创建 classes
子目录
在 WEB-INF
下创建 classes
目录。
注意:
- 名称必须小写。
- 用于存放 Java 编译后的
.class
文件(字节码)。
第四步:创建 lib
子目录(可选)
在 WEB-INF
下创建 lib
目录。
注意:
- 名称必须小写。
- 用于存放第三方 JAR 包(如数据库驱动等)。
- 并非必须存在。
第五步:创建 web.xml
配置文件
在 WEB-INF
目录下创建 web.xml
文件。
注意:
- 必须存在。
- 文件名必须为
web.xml
。 - 用于配置请求路径与 Servlet 类之间的映射关系。
- 推荐从已有项目复制模板,避免手动编写。
示例模板:
|
|
第六步:编写 Servlet 程序
创建一个 Java 类,实现 jakarta.servlet.Servlet
接口。
注意:
- 该接口属于 Jakarta EE 规范,不在 JDK 中。
- Tomcat 提供了对应的 API(位于
servlet-api.jar
中)。 - 从 Jakarta EE 9 开始,包名从
javax.servlet
变更为jakarta.servlet
。 - 源代码位置无特殊要求,只需确保编译后的
.class
文件放入WEB-INF/classes
目录即可。
第七步:编译 Servlet
设置环境变量 CLASSPATH
,以便编译时能找到 Servlet
接口。
|
|
注意:
- 该设置仅用于编译,不影响 Tomcat 运行时行为。
第八步:将编译后的 .class
文件拷贝到 WEB-INF/classes
将 HelloServlet.class
文件复制到 WEB-INF/classes
目录中。
第九步:在 web.xml
中注册 Servlet
在 web.xml
中添加 <servlet>
和 <servlet-mapping>
标签,完成 URL 路径与类的绑定。
示例配置:
|
|
说明:
<servlet-name>
必须一致。<url-pattern>
必须以/
开头。
第十步:启动 Tomcat 服务器
运行 Tomcat 启动脚本,启动服务器。
第十一步:访问 Servlet
在浏览器中输入完整的 URL 地址访问:
|
|
注意:
- 请求路径需包含项目名(如
/crm
)。 - 必须与
web.xml
中的<url-pattern>
完全一致。
二、HTML 页面的放置位置
HTML、CSS、JS、图片等静态资源应放在 WEB-INF
外部。
原因:
WEB-INF
目录受保护,外部无法直接访问其内容。
三、Web 应用目录结构示例
|
|
四、Servlet 执行流程简述
- 用户在浏览器输入 URL:
http://127.0.0.1:8080/crm/hello
- Tomcat 截取路径
/crm/hello
,定位到项目crm
- Tomcat 在
web.xml
中查找/hello
对应的 Servlet 类 - Tomcat 通过反射机制创建 Servlet 实例
- Tomcat 调用该实例的
service()
方法处理请求
五、总结
- Web 应用的结构需符合 Servlet 规范。
WEB-INF
是核心目录,包含配置文件和类文件。- 编写 Servlet 需要引入
servlet-api.jar
。 - 不需要自己编写
main
方法,由 Tomcat 负责调用。 - 请求路径必须与
web.xml
中的<url-pattern>
保持一致。
如需进一步了解 Jakarta EE 规范和相关 XML Schema,请参考官方文档:
关于 JavaEE 的版本演进
一、JavaEE 的发展历史
- 最高版本:JavaEE 的最终版本为 Java EE 8。
- 归属变更:Oracle 宣布将 JavaEE 规范捐献给 Apache 基金会。
二、名称变更与后续发展
- 品牌迁移:Apache 接手后,不再使用 “JavaEE” 这一名字。
- 新名称:新的企业级 Java 规范被称为 Jakarta EE。
- 未来命名规则:从 Java EE 8 之后的版本开始,统一以 Jakarta EE + 版本号 形式命名。
三、版本对应关系与包名变化
JavaEE 版本 | 对应 Jakarta EE 版本 | Servlet 包路径 |
---|---|---|
Java EE 8 | - | javax.servlet.Servlet |
- | Jakarta EE 9 | jakarta.servlet.Servlet |
⚠️ 注意:
在 Jakarta EE 9 及以后版本中,所有类的包名都由javax
改为了jakarta
。这意味着:
- 使用
javax.servlet.Servlet
的旧项目无法直接部署在支持 Jakarta EE 的服务器上(如 Tomcat 10+)。- 若需兼容性支持,请使用 Tomcat 9 或更早版本。
四、部署建议
- 如果你的项目仍在使用
javax
包:- 可部署环境:Tomcat 9 及其以下版本。
- 不可部署环境:Tomcat 10 及以上版本。
- 如需迁移到 Tomcat 10+,请将代码中的
javax
包替换为jakarta
。
解决Tomcat服务器在DOS命令窗口中的乱码问题(控制台乱码)
将CATALINA_HOME/conf/logging.properties文件中的内容修改如下:
|
|
向浏览器响应一段HTML代码
|
|
在Servlet中连接数据库,怎么做?
- Servlet是Java程序,所以在Servlet中完全可以编写JDBC代码连接数据库。
- 在一个webapp中去连接数据库,需要将驱动jar包放到WEB-INF/lib目录下。(com.mysql.cj.jdbc.Driver 这个类就在驱动jar包当中。)
在集成开发环境(IDE)中开发 Servlet 程序
一、常用的集成开发工具简介
目前主流的 Java 集成开发工具有两种:
1. IntelliJ IDEA
- 使用人数较多,提示功能优于 Eclipse。
- 更加智能,用户体验更佳。
- 由 JetBrains 公司开发。
- 商业软件(收费)。
2. Eclipse
- 使用人数相对较少,但仍有一定用户基础。
- 正处于逐渐被替代的趋势。
- 由 IBM 团队开发。
- 名称寓意为“日食”,象征意图吞并 SUN 公司。
- 实际上,SUN 公司于 2009 年被 Oracle 收购,IBM 并未实现并购目标。
二、使用 IntelliJ IDEA 开发 Servlet 的步骤
第一步:创建空项目(Empty Project)
- 打开 IDEA →
File
→New
→Project
- 选择
Empty Project
- 命名项目为:
javaweb
(非强制,建议与目录一致) - 可选操作:后续在此项目下添加模块(Module)
第二步:新建模块(Module)
File
→New
→Module
- 创建一个普通的 JavaSE 模块(不要选 Java Enterprise)
- 模块名建议为:
servlet01
第三步:将模块转换为 Web Application
- 右键点击模块 →
Add Framework Support...
- 选择
Web Application
- IDEA 将自动生成符合 Servlet 规范的 webapp 目录结构
- 注意:web 目录即为 webapp 的根目录
第四步(可选):删除默认生成的 index.jsp 文件
- 根据需要删除模板生成的
index.jsp
文件
第五步:编写 Servlet 类
示例类名:StudentServlet
|
|
-
如果出现找不到
Servlet.class
的错误:- 进入
File
→Project Structure
→Modules
- 点击
+
添加 JAR 包 - 添加 Tomcat 中的
servlet-api.jar
和jsp-api.jar
到 classpath
- 进入
-
实现
jakarta.servlet.Servlet
接口中的五个方法
第六步:在 service 方法中编写业务逻辑
- 可以在这里添加数据库连接等具体业务代码
第七步:添加数据库驱动包
- 在
WEB-INF
下新建目录:lib
(必须全小写) - 将数据库驱动 JAR 包放入此目录
第八步:配置 web.xml 注册 Servlet
文件路径:WEB-INF/web.xml
|
|
第九步:创建 HTML 页面用于测试
- 文件名:
student.html
- 必须放在 WEB-INF 外部
- 示例内容如下:
|
|
第十步:配置 IDEA 关联 Tomcat 并部署项目
- 点击右上角的绿色锤子旁的按钮:
Add Configuration
- 点击左上角的
+
→Tomcat Server
→Local
- 设置服务器参数(一般无需修改)
- 切换到
Deployment
选项卡- 点击
+
添加部署 - 设置
Application context
为/xmm
- 点击
第十一步:启动 Tomcat 服务器
- 点击右上角绿色箭头或虫子图标(Debug 启动)
- 推荐使用 Debug 模式启动 Tomcat,方便调试
第十二步:访问测试页面
- 打开浏览器,输入地址:
|
|
三、总结
通过上述步骤,我们完成了在 IntelliJ IDEA 中开发一个简单 Servlet 应用的全过程。包括项目创建、Servlet 编写、配置部署以及最终的测试运行。整个流程遵循标准的 Java Web 开发规范,适合初学者快速入门。
Servlet 对象的生命周期
一、什么是 Servlet 生命周期?
Servlet 的生命周期是指一个 Servlet 对象从创建到销毁的全过程。主要包括以下几个阶段:
- 何时被创建?
- 何时被销毁?
- 创建了多少个实例?
- 整个过程是怎样的?
二、Servlet 生命周期由谁管理?
- Servlet 对象的创建、方法调用和销毁均由 Tomcat服务器(WEB Server) 全权负责。
- Tomcat 又被称为 WEB 容器(Web Container)。
- 只有容器中管理的 Servlet 对象才能被正常调用。
- 自己手动
new
出来的 Servlet 对象 不会被 WEB 容器管理。
关键点:
- Web 容器内部通常使用一个类似
HashMap
的集合来维护 Servlet 对象与请求路径之间的映射关系。 - 示例图:
三、默认情况下,服务器启动时是否创建 Servlet 对象?
- 默认情况下,Servlet 在服务器启动时不会被实例化。
- 构造方法不会执行。
- 这种设计合理:避免浪费内存资源,未访问的 Servlet 不必提前创建。
四、如何让服务器在启动时创建 Servlet?
可以通过配置 web.xml
文件中的 <load-on-startup>
标签实现:
|
|
- 数值越小,优先级越高。
- 此配置表示该 Servlet 应在服务器启动时加载并初始化。
五、Servlet 生命周期详解
1. 第一次请求发生时的行为
控制台输出如下:
|
|
结论:
- 用户第一次发送请求时,Servlet 被实例化(调用了无参构造方法)。
- 实例化后立即调用
init()
方法。 - 紧接着调用
service()
方法处理请求。
2. 后续请求行为
用户再次发送请求时,控制台输出如下:
|
|
结论:
- Servlet 是单例对象(但不是真正的单例模式)。
service()
方法每次请求都会被调用。构造方法
和init()
只会在第一次请求时执行一次。
3. 服务器关闭时的行为
控制台输出如下:
|
|
结论:
destroy()
方法只执行一次,在服务器关闭前被调用。- 用于释放资源、保存数据等操作。
destroy()
执行完毕后,Servlet 对象才被销毁。
4. 生命周期类比人生阶段
方法 | 比喻 |
---|---|
构造方法 | 出生 |
init() |
接受教育 |
service() |
工作服务 |
destroy() |
临终 |
六、Servlet 方法执行次数总结
方法 | 执行次数 |
---|---|
构造方法 | 1次(仅第一次请求) |
init() |
1次(仅第一次请求) |
service() |
每次请求都执行 |
destroy() |
1次(服务器关闭时) |
七、常见问题与注意事项
1. 如果自定义有参构造方法而没有无参构造方法会发生什么?
- 报错:HTTP 500 错误(服务器内部错误)
- 原因:Servlet 容器只能通过无参构造方法创建对象
- 建议:不要在 Servlet 中定义构造方法
2. 能否用构造方法代替 init()
方法?
- 不能。
- 虽然两者都只执行一次,但构造方法可能影响 Servlet 实例化。
init()
更适合做初始化操作(如连接池、线程池初始化)
3. 哪些方法最常被使用?
service()
方法最重要,必须重写。init()
和destroy()
方法较少使用,但可用于资源初始化和清理。
GenericServlet 概述
一、直接实现 Servlet
接口的缺点
当我们编写一个 Servlet 类直接实现 Servlet
接口时,存在以下问题:
- 只关心
service()
方法,而其他方法(如init()
、destroy()
等)在大多数情况下并不需要使用。 - 导致代码冗余且不够优雅。
二、适配器设计模式(Adapter Pattern)
示例:充电器作为适配器
- 手机无法直接连接 220V 电压,否则会损坏。
- 使用一个充电器作为中间适配器:
- 手机连接充电器;
- 充电器连接电源;
- 实现安全供电。
在 Servlet 中的应用
GenericServlet
是一个实现了Servlet
接口的抽象类;- 它充当了一个“适配器”,屏蔽了不需要频繁重写的接口方法;
- 用户只需要关注并重写
service()
方法即可。
三、GenericServlet 的作用与使用方式
1. GenericServlet
的特点
- 是一个抽象类;
- 实现了
Servlet
接口; - 提供了一个抽象的
service()
方法; - 其他方法(如
init()
、destroy()
)提供默认空实现。
2. 使用方式
- 开发者只需继承
GenericServlet
; - 并重写其
service()
方法; - 无需处理不相关的生命周期方法。
四、关于 GenericServlet 的几个思考问题
1. init()
方法还会执行吗?
- 是的,会执行。
- 执行的是
GenericServlet
类中的init()
方法。
2. init()
方法是谁调用的?
- 是由 Tomcat 服务器自动调用的。
3. ServletConfig
对象是谁创建并传入的?
- 是由 Tomcat 服务器负责创建并传递给
init()
方法的。
五、Tomcat 服务器伪代码示例
以下是一个简化的 Tomcat 启动和调用 Servlet 的伪代码,帮助理解整个过程:
|
|
通过上述分析可以看出,GenericServlet
为我们简化了 Servlet 编程模型,提高了开发效率,同时遵循了“适配器”设计模式的思想。
ServletConfig 概述
1. 什么是 ServletConfig?
ServletConfig
是一个用于封装 Servlet 对象配置信息 的对象。- 它主要用来保存在
web.xml
文件中<servlet>
标签内定义的配置信息。
2. 与 Servlet 的关系
- 每一个 Servlet 都有唯一一个对应的 ServletConfig 对象。
- 该对象由 Tomcat(Web)服务器创建,默认情况下,在用户发送第一次请求时创建。
3. 初始化过程
-
Tomcat 在调用
Servlet
的init()
方法时,会将ServletConfig
对象作为参数传递进去:1
public void init(ServletConfig config) throws ServletException
4. 实现细节
ServletConfig
接口的具体实现类是由 Tomcat 服务器提供的。- 因为
GenericServlet
类已经实现了ServletConfig
接口,所以在自定义的 Servlet 中可以直接使用this
来调用其方法。
5. 常用方法
以下是一些常用的 ServletConfig
接口方法:
方法名 | 描述 |
---|---|
String getInitParameter(String name) |
通过初始化参数的名称获取对应的值 |
Enumeration<String> getInitParameterNames() |
获取所有初始化参数的名称集合 |
ServletContext getServletContext() |
获取当前 Servlet 所属的 ServletContext 对象 |
String getServletName() |
获取当前 Servlet 的名称(即 <servlet-name> 的值) |
这些方法可以在 Servlet 类中直接使用,例如:
|
|
ServletContext 详解
一、基本概念
1. 什么是 ServletContext?
- 每个 Web 应用(webapp)只有一个
ServletContext
对象。 - 所有该应用中的 Servlet 共享同一个
ServletContext
。 - 它在服务器启动时创建,在服务器关闭时销毁,是应用级对象。
2. 类比生活场景
- 可将
ServletContext
比作一个“教室”:- 教室中有多个学生(Servlet);
- 教室中有一个空调(共享资源),所有学生都可以使用;
- 这个“教室”就是
ServletContext
,它保存的是整个应用的共享环境与数据。
3. 接口实现
ServletContext
是一个接口;- Tomcat 等服务器对其进行了实现;
- 启动 Web 应用时由服务器自动创建。
二、配置与初始化参数
1. 获取初始化参数
在 web.xml
中通过 <context-param>
标签配置全局参数,可通过以下方法获取:
|
|
示例配置:
|
|
⚠️ 注意:这些是应用级配置信息。若只想为某个 Servlet 配置参数,请使用
<servlet>
标签内的配置,并通过ServletConfig
获取。
三、常用方法
1. 获取应用根路径
|
|
用于动态获取当前 Web 应用的上下文路径,避免硬编码。
2. 获取文件真实路径
|
|
将虚拟路径转换为服务器上的绝对路径。
3. 记录日志
|
|
日志记录到如下位置(Tomcat):
logs/localhost.<date>.log
:记录ServletContext.log()
的输出;logs/catalina.<date>.log
:记录服务器控制台输出;logs/localhost_access_log.<date>.txt
:记录访问日志。
4. 应用域(Application Scope)
ServletContext
也称为“应用域”,适用于以下情况的数据存储:
- 所有用户共享
- 数据量小
- 几乎不修改
存储与读取数据的方法:
|
|
✅ 优点:提高执行效率,减少数据库访问;
❌ 缺点:占用内存时间长,不适合大数据或频繁修改的数据。
四、Servlet 继承体系简介
我们通常不会直接继承 GenericServlet
,而是使用其子类 HttpServlet
来处理 HTTP 请求。
继承结构如下:
|
|
使用建议:
- 我们编写的 Servlet 类应继承
HttpServlet
; - 更适合处理基于 HTTP 协议的请求。
五、缓存机制回顾
缓存类型 | 描述 |
---|---|
字符串常量池 | 存放 "abc" 等字符串,避免重复创建 |
整数型常量池 | 范围 [-128, 127] 的 Integer 对象 |
连接池(Connection Pool) | 提前创建多个数据库连接对象,提升访问效率 |
线程池(Thread Pool) | 提前创建多个线程,处理并发请求 |
Redis | NoSQL 缓存数据库,适用于分布式系统 |
ServletContext 应用域 | 存放应用级共享数据,也是一种缓存机制 |
总结
ServletContext
是 Web 应用的核心对象之一;- 生命周期与应用一致;
- 适合存放只读、小数据量、共享的信息;
- 提供了丰富的 API 方法,如获取路径、记录日志、操作域属性等;
- 在实际开发中,结合其他缓存机制可显著提升系统性能。
HTTP 协议详解
一、什么是协议?
-
定义:
- 协议是由某些人或组织制定的一套规范,所有人都按照这套规范进行交流,以实现沟通无障碍。
- 协议就是标准,是一种大家共同遵守的规则。
-
举例说明:
- 比如我们使用的“普通话”,就是一种语言协议。我们都使用普通话交流,彼此就能听懂对方的话。
二、什么是 HTTP 协议?
1. 简介
- HTTP 协议:是 W3C 制定的一种超文本传输协议。
- 作用:用于浏览器(B)与服务器(S)之间的数据交互。
- 特点:
- 支持传输普通文本、图片、音频、视频等流媒体信息。
- 浏览器向服务器发送请求时遵循该协议,服务器返回响应也需遵循该协议。
2. W3C 组织简介
- W3C:
- 全称:万维网联盟
- 职责:制定 Web 相关标准(如 HTML、XML、HTTP、DOM 等)
- 创始人:蒂姆·伯纳斯·李(Tim Berners-Lee)
3. 超文本(Hypertext)
- 不只是普通的文字,还可以包含图像、声音、视频等多媒体内容。
- HTTP 支持这些内容的传输。
4. 解耦合机制
- 浏览器不依赖具体服务器品牌。
- 服务器也不依赖具体浏览器品牌。
- 实现了 B/S 架构的松耦合通信。
三、HTTP 请求协议(Browser → Server)
1. 结构组成
HTTP 请求由四部分组成:
部分 | 描述 |
---|---|
请求行 | 包括请求方式、URI 和协议版本号 |
请求头 | 包含客户端的各种附加信息 |
空白行 | 分隔请求头与请求体 |
请求体 | 发送给服务器的数据(POST 特有) |
2. 示例:GET 请求
|
|
3. 示例:POST 请求
|
|
4. 各组成部分详解
(1)请求行(Request Line)
- 包括三部分:
- 请求方式(7种常见):
GET
(常用)POST
(常用)DELETE
、PUT
、HEAD
、OPTIONS
、TRACE
- URI(统一资源标识符):
- 表示资源名称,但不能定位资源。
- URL(统一资源定位符):
- 包含 URI,同时可以定位资源。
- 示例:
- URL:
http://localhost:8080/servlet05/index.html
- URI:
/servlet05/index.html
- URL:
- 协议版本号:
- 如
HTTP/1.1
- 如
- 请求方式(7种常见):
(2)请求头(Headers)
- 包含客户端信息,例如:
- 主机名和端口
- 浏览器类型
- 平台信息
- Cookie 数据等
(3)空白行(Empty Line)
- 用来分隔请求头和请求体。
(4)请求体(Body)
- 只在 POST 请求中出现。
- 存放要发送给服务器的具体数据。
四、HTTP 响应协议(Server → Browser)
1. 结构组成
HTTP 响应同样由四部分组成:
部分 | 描述 |
---|---|
状态行 | 包括协议版本、状态码和描述 |
响应头 | 包含服务器返回的各种附加信息 |
空白行 | 分隔响应头与响应体 |
响应体 | 返回给浏览器的具体内容 |
2. 示例:响应报文
|
|
3. 各组成部分详解
(1)状态行(Status Line)
- 包括三部分:
- 协议版本号
- 状态码
200
:成功404
:资源未找到(前端错误)405
:请求方法不支持500
:服务器内部错误- 以
4xx
开头:客户端错误 - 以
5xx
开头:服务端错误
- 状态描述
- 如
OK
、Not Found
、Internal Server Error
- 如
(2)响应头(Headers)
- 包括:
- 内容类型(Content-Type)
- 内容长度(Content-Length)
- 响应时间(Date)
- 连接方式(Connection)等
(3)空白行(Empty Line)
- 分隔响应头和响应体。
(4)响应体(Body)
- 是服务器返回的具体内容,如 HTML 页面、JSON 数据等。
五、如何查看 HTTP 协议内容?
- 使用 Chrome 浏览器:
- 按下
F12
打开开发者工具 - 切换到
Network
标签页 - 刷新页面即可查看每个请求的详细协议内容
- 按下
六、GET 与 POST 请求的区别
对比项 | GET 请求 | POST 请求 |
---|---|---|
数据位置 | 在请求行中(URI后) | 在请求体中 |
安全性 | 安全(仅获取数据) | 不安全(提交数据) |
缓存支持 | 支持缓存 | 不支持缓存 |
数据长度限制 | 有限制(不同浏览器不同) | 无限制 |
数据类型 | 只能发送普通字符串 | 可以发送任何类型数据(包括文件、图片等) |
用途建议 | 获取服务器资源 | 向服务器提交数据 |
敏感信息暴露 | 会暴露在地址栏 | 不暴露 |
七、GET 和 POST 的使用场景
-
GET 请求适用场景:
- 获取数据
- 不涉及敏感信息
- 不需要修改服务器资源
- 需要缓存结果
-
POST 请求适用场景:
- 提交表单数据
- 上传文件
- 修改服务器资源
- 传递敏感信息(如密码)
八、请求数据格式统一
- 不论是 GET 还是 POST 请求,数据格式都是统一的:
1
name=value&name=value&name=value
- name:通常是表单字段的
name
属性 - value:对应字段的输入值
总结
HTTP 是现代 Web 应用中最基础的通信协议,通过统一的请求和响应格式,实现了浏览器与服务器之间的高效、标准化通信。GET 和 POST 是最常用的两种请求方式,各有其适用场景。掌握它们的特点和区别,有助于更好地开发和调试 Web 应用。
模板方法设计模式
一、什么是设计模式?
- 设计模式是针对某一类常见问题的标准化解决方案。
- 它具有可重用性、可维护性和可扩展性。
二、常见的设计模式分类
1. GoF 设计模式(Gang of Four)
- 定义:由四位软件工程专家提出的23种经典设计模式。
- 常见模式包括:
- 单例模式
- 工厂模式
- 代理模式
- 门面模式
- 责任链模式
- 观察者模式
- 模板方法模式
- ……(还有其他多种)
2. JavaEE 设计模式
- 主要用于企业级开发中数据与业务逻辑的分离。
- 常见模式包括:
- DAO(Data Access Object)
- DTO(Data Transfer Object)
- VO(Value Object)
- PO(Persistent Object)
- POJO(Plain Old Java Object)
- ……
3. 其他设计模式
- 还有架构模式、行为模式等不同分类,适用于不同的场景。
三、模板方法设计模式详解
1. 定义
在模板类的模板方法中定义核心算法骨架,而具体的实现步骤延迟到子类中完成。
2. 核心特点
- 模板类通常为抽象类。
- 模板方法中定义了核心算法流程,该方法通常是
final
的(防止被重写)。 - 抽象方法则代表不确定实现的具体步骤,需由子类根据需求实现。
3. 示例说明(简要)
|
|
四、总结
- 模板方法模式通过封装算法流程,使得具体实现可灵活扩展。
- 是一种行为型设计模式,常用于框架设计中,提供统一接口的同时允许子类定制实现。
HttpServlet 源码分析
一、HttpServlet 简介
HttpServlet
是专为 HTTP 协议设计的 Servlet 类,相较于GenericServlet
更适合 HTTP 协议下的开发。- 包路径:
jakarta.servlet.http.HttpServlet
二、Servlet 规范中的主要接口与类
我们已接触的接口(jakarta.servlet.*)
类/接口 | 说明 |
---|---|
Servlet |
核心接口 |
ServletConfig |
配置信息接口 |
ServletContext |
上下文接口 |
ServletRequest |
请求接口 |
ServletResponse |
响应接口 |
ServletException |
异常类 |
GenericServlet |
通用抽象类 |
HTTP 包中的类与接口(jakarta.servlet.http.*)
类/接口 | 说明 |
---|---|
HttpServlet |
HTTP 专用的抽象类 |
HttpServletRequest |
HTTP 请求对象 |
HttpServletResponse |
HTTP 响应对象 |
三、HttpServletRequest 和 HttpServletResponse
HttpServletRequest
- 又称
request
对象 - 封装了请求协议的所有内容
- Tomcat 服务器将 HTTP 请求数据解析并封装到该对象中
- 开发者可通过此对象获取请求信息
HttpServletResponse
- 用于向浏览器发送响应数据
- 主要处理 HTTP 响应逻辑
四、Servlet 生命周期回顾
-
用户第一次请求
- Tomcat 通过反射创建 Servlet 实例(调用无参构造方法)
- 调用
init(ServletConfig)
方法进行初始化 - 调用
service()
处理请求
-
后续请求
- 直接调用
service()
方法处理请求
- 直接调用
-
服务器关闭
- 调用
destroy()
方法执行销毁前的清理工作 - 最终销毁 Servlet 对象
- 调用
五、HttpServlet 源码剖析
|
|
HttpServlet 的核心方法:service
1. service(ServletRequest, ServletResponse)
|
|
2. service(HttpServletRequest, HttpServletResponse)
|
|
总结:
service()
是一个模板方法,定义了处理请求的核心骨架,具体实现由子类完成。
默认的 doGet / doPost 方法
|
|
如果未重写对应的方法,将会抛出 405 错误(HTTP 405 Method Not Allowed)。
六、常见问题解答
1. 如果前端请求方式和后端重写方法不匹配会发生什么?
- 若前端发送的是
GET
请求,而后端只重写了doPost
,则会触发 405 错误。 - 同理,若前端发送的是
POST
请求,而后端只重写了doGet
,也会触发 405 错误。
2. 如何避免 405 错误?
- 后端重写
doGet
,前端必须发送GET
请求; - 后端重写
doPost
,前端必须发送POST
请求; - 请求方式由后端决定,前端需配合使用正确的请求方式。
建议:不要为了规避 405 错误而同时重写
doGet
和doPost
。如果确实需要统一处理,可直接重写service()
方法。
3. 是否可以直接重写 HttpServlet 的 service() 方法?
- 可以,但将失去对请求方式的自动判断,也无法享受 HTTP 专属功能。
- 不推荐,除非有特殊需求。
七、Servlet 开发标准流程
- 第一步:编写 Servlet 类,继承
HttpServlet
- 第二步:根据业务需求重写
doGet
或doPost
方法 - 第三步:在
web.xml
中配置该 Servlet 映射路径 - 第四步:准备前端页面(如 HTML 表单),指定对应的请求路径
💡 提示:理解
HttpServlet
的源码有助于掌握 Java Web 开发底层机制,合理利用框架提供的功能,提升开发效率和代码质量。
Web 站点的欢迎页面详解
一、什么是 Web 站点的欢迎页面?
- 对于一个 Web 应用(WebApp)来说,我们可以设置它的欢迎页面。
- 设置了欢迎页面后,当用户访问该 WebApp 或站点时,没有指定具体资源路径,系统会默认跳转到欢迎页面。
示例说明:
-
指定了资源路径:
1
http://localhost:8080/servlet06/login.html
-
未指定资源路径(访问欢迎页):
1
http://localhost:8080/servlet06
此时,默认会访问你在配置中指定的欢迎页面。
二、如何设置欢迎页面?
步骤 1:在 web
目录下创建登录页面
例如,在 IDEA 的 web 目录下新建一个文件:
|
|
步骤 2:在 web.xml
中配置欢迎页面
|
|
⚠️ 注意事项:
- 路径不需要以
/
开头; - 默认从 WebApp 的根目录开始查找资源。
步骤 3:启动服务器并访问站点
访问地址如下:
|
|
此时将自动加载 login.html
页面。
三、如果欢迎页面在子目录中该如何设置?
场景描述:
- 在 WebApp 根目录下新建目录结构:
1
/page1/page2/page.html
配置方式:
|
|
⚠️ 同样地,路径不需要以
/
开头,查找仍从 WebApp 根目录开始。
四、一个 WebApp 可以设置多个欢迎页面吗?
是的!可以设置多个欢迎页面,并按优先级顺序排列:
|
|
⚠️ 优先级规则:
越靠上的<welcome-file>
优先级越高。如果第一个找不到,则继续向下查找。
五、为什么 index.html
不需要配置也能作为欢迎页面?
这是因为 Tomcat 服务器已经预设了全局的欢迎页面配置。
欢迎页面的配置位置有两个:
- 局部配置:在你自己的 WebApp 的
WEB-INF/web.xml
文件中; - 全局配置:在 Tomcat 的
CATALINA_HOME/conf/web.xml
文件中。
Tomcat 默认的欢迎页面配置:
|
|
⚠️ 配置原则:
遵循“局部优先”原则。如果你自己设置了欢迎页面,Tomcat 就不会使用默认的index.html
等页面。
六、欢迎页面可以是一个 Servlet 吗?
当然可以!
欢迎页面本质上就是一个资源,既可以是静态资源(如 HTML),也可以是动态资源(如 Servlet)。
步骤 1:编写一个欢迎用的 Servlet
|
|
步骤 2:在 web.xml
中注册该 Servlet
|
|
步骤 3:在 web.xml
中配置为欢迎页面
|
|
这样,当你访问站点根路径时,就会触发这个 Servlet 并输出欢迎信息。
总结
类型 | 资源示例 | 是否需配置 |
---|---|---|
静态资源 | login.html , index.html |
是(除 index.html 等默认值) |
动态资源 | WelcomeServlet |
是 |
✅ 最佳实践建议:
- 使用
index.html
可省去配置; - 多个欢迎页面按优先级排列;
- 欢迎页面可灵活选择静态或动态资源。
关于WEB-INF目录
- 在WEB-INF目录下新建了一个文件:welcome.html
- 打开浏览器访问:http://localhost:8080/servlet07/WEB-INF/welcome.html 出现了404错误。
- 注意:放在WEB-INF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外。
HttpServletRequest 接口详解
一、基本概述
1. 接口定义
- 全限定名:
jakarta.servlet.http.HttpServletRequest
- 所属规范:Servlet 规范的一员
- 父接口:
ServletRequest
|
|
2. 实现与创建
- 实现类:Tomcat 中为
org.apache.catalina.connector.RequestFacade
- 对象创建者:Tomcat 服务器(即 Web 容器)
- 说明:
- Tomcat 实现了
HttpServletRequest
接口,并负责创建其对象。 - 开发者无需关心具体实现类,只需面向接口编程。
- Tomcat 实现了
3. 封装内容
- 封装信息:HTTP 请求协议中的所有信息
- 数据来源:浏览器发送的 HTTP 请求
- 处理流程:
- 浏览器发送 HTTP 请求;
- Tomcat 解析请求;
- 将解析结果封装到
HttpServletRequest
对象中; - 提供给 JavaWeb 程序员使用。
4. 生命周期
- request 和 response 的生命周期:
- 仅在当前请求中有效;
- 每次请求都会创建新的 request;
- 多次请求对应多个独立的 request。
二、常用方法详解
1. 获取用户提交的数据
常用方法列表:
|
|
数据存储结构分析:
- 表单示例:
username=abc&userpwd=111&aihao=s&aihao=d&aihao=tt
- 存储结构应为:
Map<String, String[]>
- 举例:
1 2 3 4 5
key | value ------------------------------- username | ["abc"] userpwd | ["111"] aihao | ["s", "d", "tt"]
- 举例:
注意事项:
- 所有前端提交的数据均为字符串类型;
- 即使是数字(如
age=120
),后端获取到的也是字符串"120"
。
2. 请求域(Request Scope)
概述:
request
是一个“请求域”对象;- 用于在一次请求中共享数据;
- 生命周期短于应用域(
ServletContext
)。
域操作方法:
|
|
与应用域对比:
类型 | 范围 | 生命周期 | 使用场景 |
---|---|---|---|
RequestScope | 当前请求 | 一次请求结束销毁 | 同一请求内多个组件共享 |
AppScope | 整个 Web 应用 | 应用启动至关闭期间 | 所有用户共享数据 |
使用建议:
- 优先使用范围小的域对象,减少资源占用。
3. 请求转发(Forward)
示例代码:
|
|
特点:
- 一次请求;
- 可以转发给任意合法资源(Servlet、HTML、JSP 等);
- 路径写法:以
/
开头,不加项目名。
数据共享方式:
- 在 AServlet 中设置属性,转发至 BServlet,可共享 request 域中的数据。
4. 参数获取与属性获取的区别
方法 | 来源 | 描述 |
---|---|---|
getParameter() |
用户提交的数据 | 获取请求参数(如表单、URL 参数) |
getAttribute() |
请求域中的属性 | 获取通过 setAttribute() 设置的值 |
三、其他常见方法
|
|
四、乱码问题解决方案
请求方式 | 乱码原因 | 解决方案 |
---|---|---|
GET | 请求行中的参数编码 | 修改 server.xml 配置:URIEncoding="UTF-8" |
POST | 请求体中的编码 | request.setCharacterEncoding("UTF-8") |
Response | 响应内容编码 | response.setContentType("text/html;charset=UTF-8") |
注意:
- Tomcat 10+ 默认使用 UTF-8 编码,不再需要手动设置;
- 这体现了对中文支持的增强。
五、缓存技术简要介绍
技术名称 | 描述 |
---|---|
字符串常量池 | 相同字符串复用,提高性能 |
整数常量池 | [-128~127] 内整数复用 |
数据库连接池 | 预先创建连接,提升数据库访问效率 |
线程池 | 预先创建线程,提高并发处理能力 |
Redis / MongoDB | 分布式缓存,适用于大规模系统 |
六、总结
HttpServletRequest
是 Servlet 规范的重要组成部分;- 封装了 HTTP 请求的所有信息;
- 提供丰富的 API 用于获取请求参数、设置域属性、实现转发等功能;
- 合理使用 request 域和应用域,可以提升系统性能;
- 注意乱码问题的处理策略,尤其在不同 Tomcat 版本中的差异;
- 掌握缓存思想,有助于构建高性能 Web 应用。
使用纯Servlet实现部门表的CRUD操作
一、项目概述
本项目使用纯Servlet技术完成对部门表(dept
)的增删改查(CRUD)操作,采用B/S架构。
二、数据库准备
部门表结构及初始化脚本
|
|
三、前端页面设计
使用 HBuilder 开发工具创建HTML页面,主要包括以下页面:
页面名称 | 描述 |
---|---|
index.html |
欢迎页面 |
list.html |
部门列表页(核心页面) |
add.html |
新增部门页面 |
edit.html |
修改部门页面 |
detail.html |
查看部门详情页面 |
四、功能分析
每个连接数据库的操作即为一个独立功能。系统包括以下6个功能:
- 查看部门列表
- 新增部门
- 删除部门
- 查看部门详细信息
- 跳转到修改页面
- 修改部门
五、开发环境搭建(IDEA)
步骤如下:
- 创建Web应用项目。
- 添加依赖:
servlet-api.jar
jsp-api.jar
- MySQL驱动包(放入
WEB-INF/lib
目录)
- 编写JDBC工具类用于数据库连接。
- 将HTML页面拷贝至web目录下。
六、功能实现流程详解
1. 查看部门列表
实现步骤:
-
前端页面:
1
<a href="/oa/dept/list">查看部门列表</a>
-
配置
web.xml
:1 2 3 4 5 6 7 8
<servlet> <servlet-name>list</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptListServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>list</servlet-name> <url-pattern>/dept/list</url-pattern> </servlet-mapping>
-
编写
DeptListServlet
类:继承
HttpServlet
,重写doGet()
方法,连接数据库查询数据并动态生成 HTML 表格。示例代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
while(rs.next()) { String deptno = rs.getString("deptno"); String dname = rs.getString("dname"); String loc = rs.getString("loc"); out.print("<tr>"); out.print(" <td>" + (++i) + "</td>"); out.print(" <td>" + deptno + "</td>"); out.print(" <td>" + dname + "</td>"); out.print(" <td>"); out.print(" <a href=''>删除</a>"); out.print(" <a href='edit.html'>修改</a>"); out.print(" <a href='detail.html'>详情</a>"); out.print(" </td>"); out.print("</tr>"); }
2. 查看部门详情
实现步骤:
-
前端链接添加参数:
1
out.print("<a href='" + contextPath + "/dept/detail?deptno=" + deptno + "'>详情</a>");
-
配置
web.xml
:1 2 3 4 5 6 7 8
<servlet> <servlet-name>detail</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptDetailServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>detail</servlet-name> <url-pattern>/dept/detail</url-pattern> </servlet-mapping>
-
编写
DeptDetailServlet
类:在
doGet()
方法中获取deptno
参数,查询数据库并展示详情页面。
3. 删除部门
实现步骤:
-
前端 JS 提示确认删除:
1 2 3 4 5 6 7 8
<a href="javascript:void(0)" onclick="del(30)">删除</a> <script type="text/javascript"> function del(dno){ if(window.confirm("亲,删了不可恢复哦!")){ document.location.href = "/oa/dept/delete?deptno=" + dno; } } </script>
-
配置
web.xml
:1 2 3 4 5 6 7 8
<servlet> <servlet-name>delete</servlet-name> <servlet-class>com.bjpowernode.oa.web.action.DeptDelServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>delete</servlet-name> <url-pattern>/dept/delete</url-pattern> </servlet-mapping>
-
编写
DeptDelServlet
类:根据
deptno
删除记录,并根据结果转发到对应页面(成功跳转列表页,失败跳转错误页)。1 2 3 4 5
if (count == 1) { request.getRequestDispatcher("/dept/list").forward(request, response); } else { request.getRequestDispatcher("/error.html").forward(request, response); }
4. 新增部门
注意事项:
- 若使用 POST 请求提交新增数据,转发到
/dept/list
时可能出现 405 错误。 - 原因:转发请求仍保持原方法(POST),而目标 Servlet 只有 doGet()。
- 解决方案:
- 方案一: 在目标 Servlet 中添加
doPost()
并调用doGet()
。 - 方案二: 使用重定向代替转发。
- 方案一: 在目标 Servlet 中添加
5. 跳转到修改页面
- 用户点击“修改”按钮后,应跳转至
edit.html
页面。 - 页面需携带当前部门编号参数,供后续修改使用。
6. 修改部门
- 类似于新增功能,但需要先从数据库加载当前部门信息填充表单。
- 提交时更新数据库,并重定向或转发回列表页。
七、总结
通过以上步骤,可以使用纯Servlet技术完成对部门表的完整 CRUD 操作。虽然代码较为繁琐,但有助于深入理解 Web 应用底层工作原理。
在一个 Web 应用中如何完成资源跳转
在 Web 应用中,实现资源跳转主要有两种方式:
- 转发(Forward)
- 重定向(Redirect)
一、转发 vs 重定向
对比项 | 转发(Forward) | 重定向(Redirect) |
---|---|---|
请求次数 | 一次请求 | 两次请求 |
地址栏变化 | 不变 | 改变为最终目标地址 |
控制者 | 服务器(Tomcat) | 浏览器 |
是否共享 request 域数据 | 是 | 否 |
1. 代码上的区别
✅ 转发示例(Java)
|
|
- 说明:转发过程中始终使用同一个
request
和response
,即所有转发都在一次请求中完成。 - 特点:AServlet → BServlet → CServlet → DServlet,都共用同一个请求对象。
✅ 重定向示例(Java)
|
|
- 说明:浏览器会根据返回的状态码和位置头,重新发起一个新的请求。
- 注意:路径需要加上项目名,因为是浏览器主动发送新请求。
2. 形式上的区别
📌 转发(一次请求)
- 地址栏显示的是最初请求的 URL。
- 示例:
- 初始请求:
http://localhost:8080/servlet10/a
- 最终地址不变。
- 初始请求:
📌 重定向(两次请求)
- 地址栏最终显示的是重定向后的 URL。
- 示例:
- 初始请求:
http://localhost:8080/servlet10/a
- 最终地址:
http://localhost:8080/servlet10/b
- 初始请求:
3. 本质区别
- 转发:由服务器控制,Tomcat 内部跳转。客户端无感知,只看到一次请求。
- 重定向:由浏览器执行,相当于重新发起一个全新的请求。
4. 生活化的例子解释
🧾 转发(一次请求)—— 类似“中间人帮忙借钱”
- 杜老师找张三借钱,张三没有但去找李四借了钱再给杜老师。
- 杜老师以为钱是张三给的,整个过程只有一次请求行为。
🧾 重定向(两次请求)—— 类似“直接找别人借钱”
- 杜老师找张三借钱,张三没有,但告诉杜老师李四可以借。
- 杜老师自己去找李四借钱,这是两个独立的行为。
二、转发和重定向该如何选择?
使用场景 | 推荐方式 |
---|---|
需要传递 request 域中的数据(如属性值) | 转发(Forward) |
页面跳转后希望刷新页面不重复提交 | 重定向(Redirect) |
普通的页面跳转(如登录成功后跳转首页) | 重定向(Redirect) |
✅ 建议:除非需要保留 request 域数据,否则一律使用重定向。
三、跳转的目标资源必须是 Servlet 吗?
不是!
转发或重定向的目标资源只要是服务器内部合法的资源即可,包括:
- Servlet
- JSP
- HTML 文件
- 图片、CSS、JS 等静态资源
四、注意事项
⚠️ 转发存在的问题:浏览器刷新问题
- 如果用户刷新页面,可能会导致重复处理逻辑(如重复插入数据库),因为请求仍是原始请求。
- 所以,对于涉及业务操作的请求,建议使用重定向,避免刷新造成副作用。
总结
特性 | 转发 | 重定向 |
---|---|---|
请求次数 | 1次 | 2次 |
地址栏变化 | 否 | 是 |
request 数据是否共享 | 是 | 否 |
安全性 | 较低(暴露服务端结构) | 较高 |
适用场景 | 数据共享、内部跳转 | 表单提交后跳转、防止重复提交 |
💡 一句话总结:转发是服务器内部的事,重定向是让浏览器重新做事。
将oa项目中的资源跳转修改为合适的跳转方式
- 删除之后,重定向
- 修改之后,重定向
- 保存之后,重定向
- 重定向:
- 成功
- 失败
Servlet注解,简化配置
-
分析oa项目中的web.xml文件
- 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话web.xml文件会非常庞大,有可能最终会达到几十兆。
- 在web.xml文件中进行servlet信息的配置,显然开发效率比较低,每一个都需要配置一下。
- 而且在web.xml文件中的配置是很少被修改的,所以这种配置信息能不能直接写到java类当中呢?可以的。
-
Servlet3.0版本之后,推出了各种Servlet基于注解式开发。优点是什么?
- 开发效率高,不需要编写大量的配置信息。直接在java类上使用注解进行标注。
- web.xml文件体积变小了。
-
并不是说注解有了之后,web.xml文件就不需要了:
- 有一些需要变化的信息,还是要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式。
- 一些不会经常变化修改的配置建议使用注解。一些可能会被修改的建议写到配置文件中。
-
我们的第一个注解:
-
1
jakarta.servlet.annotation.WebServlet
-
在Servlet类上使用:@WebServlet,WebServlet注解中有哪些属性呢?
- name属性:用来指定Servlet的名字。等同于:
<servlet-name>
- urlPatterns属性:用来指定Servlet的映射路径。可以指定多个字符串。
<url-pattern>
- loadOnStartUp属性:用来指定在服务器启动阶段是否加载该Servlet。等同于:
<load-on-startup>
- value属性:当注解的属性名是value的时候,使用注解的时候,value属性名是可以省略的。
- 注意:不是必须将所有属性都写上,只需要提供需要的。(需要什么用什么。)
- 注意:属性是一个数组,如果数组中只有一个元素,使用该注解的时候,属性值的大括号可以省略。
- name属性:用来指定Servlet的名字。等同于:
-
-
注解对象的使用格式:
- @注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值….)
使用模板方法设计模式优化oa项目
- 上面的注解解决了配置文件的问题。但是现在的oa项目仍然存在一个比较臃肿的问题。
- 一个单标的CRUD,就写了6个Servlet。如果一个复杂的业务系统,这种开发方式,显然会导致类爆炸。(类的数量太大。)
- 怎么解决这个类爆炸问题?可以使用模板方法设计模式。
- 怎么解决类爆炸问题?
- 以前的设计是一个请求一个Servlet类。1000个请求对应1000个Servlet类。导致类爆炸。
- 可以这样做:一个请求对应一个方法。一个业务对应一个Servlet类。
- 处理部门相关业务的对应一个DeptServlet。处理用户相关业务的对应一个UserServlet。处理银行卡卡片业务对应一个CardServlet。
分析使用纯粹Servlet开发web应用的缺陷
- 在Servlet当中编写HTML/CSS/JavaScript等前端代码。存在什么问题?
- java程序中编写前端代码,编写难度大。麻烦。
- java程序中编写前端代码,显然程序的耦合度非常高。
- java程序中编写前端代码,代码非常不美观。
- java程序中编写前端代码,维护成本太高。(非常难于维护)
- 修改小小的一个前端代码,只要有改动,就需要重新编译java代码,生成新的class文件,打一个新的war包,重新发布。
- 思考一下,如果是你的话,你准备怎么解决这个问题?
- 思路很重要。使用什么样的思路去做、去解决这个问题
- 上面的那个Servlet(Java程序)能不能不写了,让机器自动生成。我们程序员只需要写这个Servlet程序中的“前端的那段代码”,然后让机器将我们写的“前端代码”自动翻译生成“Servlet这种java程序”。然后机器再自动将“java”程序编译生成"class"文件。然后再使用JVM调用这个class中的方法。
- 思路很重要。使用什么样的思路去做、去解决这个问题
B/S结构系统中的会话机制(Session机制)
在现代Web开发中,会话机制(Session) 是实现用户状态管理的重要手段。本文将详细介绍 Session 的概念、原理、应用场景以及其在 Java Web 中的具体实现方式。
一、什么是会话?
1. 基本定义
- 会话(Session):指用户从打开浏览器开始操作,到关闭浏览器结束的一系列交互过程。
- 在这个过程中,用户可能会发起多次请求,这些请求都属于同一次会话。
2. 会话对象(Session Object)
- 在服务器端,每个用户的会话都会被封装成一个 Java 对象:
HttpSession
。 - 类路径:
jakarta.servlet.http.HttpSession
(原为javax.servlet.http.HttpSession
,Java EE 9 后改为 jakarta)。
3. 请求(Request)
- 用户点击页面触发一次请求,服务器通过
HttpServletRequest
对象来处理该请求。 - 一次会话包含多个请求。
二、为什么需要 Session?
1. HTTP 协议是无状态的
- HTTP 是一种无连接、无状态的协议。
- 每次请求完成后,客户端与服务器断开连接,服务器无法直接知道用户是否继续访问。
2. 状态保持的需求
- 用户登录后,如何在整个会话期间保留登录状态?
- 如何识别不同用户的不同会话?
- 这些问题都需要通过 Session 来解决。
三、Session 的工作原理
1. 获取 Session 对象
|
|
- 当调用此方法时,服务器会根据当前请求判断是否存在对应的 Session。
- 如果不存在,则创建一个新的 Session 并返回。
- 如果存在,则返回已有的 Session。
2. Session ID 的作用
- 每个 Session 都有一个唯一的标识符:JSESSIONID。
- JSESSIONID 以 Cookie 形式保存在浏览器内存中:
- 浏览器关闭后,Cookie 被清除,Session ID 失效。
- 下次访问时,服务器会生成新的 Session。
3. Session 列表的存储结构
- 服务器内部维护一个 Map 结构:
1
Map<String sessionId, HttpSession session>
- 每次请求携带的 Session ID 用于查找对应的 Session 对象。
四、Session 与其他域对象对比
域对象 | 对应类名 | 生命周期 | 作用范围 | 典型用途 |
---|---|---|---|---|
Request | HttpServletRequest | 一次请求 | 请求级别 | 页面跳转传值 |
Session | HttpSession | 一次会话 | 用户级别 | 登录状态、用户信息 |
Application | ServletContext | 应用启动到关闭 | 应用级别 | 全局配置、缓存数据 |
1. 域对象大小关系
|
|
2. 域对象常用方法
所有域对象都支持以下方法:
setAttribute(String name, Object value)
:设置属性getAttribute(String name)
:获取属性removeAttribute(String name)
:删除属性
3. 使用原则
- 尽量使用小范围的域对象,避免资源浪费和并发冲突。
五、Session 的实现细节
1. Session ID 的传输方式
- 默认通过 Cookie 传递 Session ID(
JSESSIONID=xxxxxx
)。 - 如果浏览器禁用了 Cookie,Session ID 可以通过 URL 重写 方式传递:
1
http://localhost:8080/servlet12/test/session;jsessionid=19D1C99560DCBF84839FA43D58F56E16
2. URL 重写机制
- 开发者需手动拼接 Session ID 到 URL 中。
- 成本较高,因此大多数网站要求用户启用 Cookie。
3. Session 的销毁
- 手动销毁:
1
session.invalidate(); // 销毁当前会话
- 自动销毁:
- 默认超时时间(如 Tomcat 默认为 30 分钟),可在
web.xml
中配置。
- 默认超时时间(如 Tomcat 默认为 30 分钟),可在
六、Session 的典型应用场景
1. 用户登录认证
- 登录成功后,将用户信息存入 Session:
1
session.setAttribute("user", user);
- 后续请求中检查 Session 是否存在用户信息,决定是否放行或跳转至登录页。
2. 购物车功能
- 临时保存用户选中的商品信息,直到用户提交订单。
3. 防止重复提交
- 在提交操作前检查 Session 中是否已有标记,防止用户重复提交。
七、Session 的优缺点分析
优点 | 缺点 |
---|---|
实现简单,易于使用 | 存储在服务器,占用内存资源 |
支持跨页面数据共享 | Session 丢失可能导致状态异常 |
提供了安全的数据存储机制 | 不适合存储大量数据 |
八、Session 与 Token 的比较(扩展知识)
随着前后端分离和移动端的发展,越来越多项目采用 Token(如 JWT) 替代传统的 Session:
对比项 | Session | Token |
---|---|---|
存储位置 | 服务器 | 客户端(如 LocalStorage) |
可扩展性 | 较差(依赖服务器内存) | 更好(无状态,便于分布式部署) |
安全性 | 依赖 Cookie | 可加密签名 |
性能影响 | 有状态,每次请求需查 Session | 无状态,性能更高 |
✅ 建议:对于大型分布式系统,推荐使用 Token;而对于传统单体 Web 应用,Session 仍是实用选择。
九、总结
Session 是 B/S 架构中实现用户状态管理的核心机制之一,具有以下特点:
- 基于 HTTP 无状态协议之上,提供状态保持能力;
- 通过 Session ID 识别用户,服务器维护 Session 数据;
- 适用于用户登录、购物车、权限控制等场景;
- 有生命周期限制,可手动或自动销毁;
- 与 Cookie 紧密相关,但也可通过 URL 重写实现;
- 在 Java Web 中由
HttpSession
接口实现; - 开发中应合理使用 Session,优先考虑较小的域对象。
如需进一步深入 Session 相关内容,可以研究如下方向:
- Session 的集群同步机制(如 Redis 存储 Session)
- Session 的持久化(如数据库存储)
- Spring Session 的使用
- OAuth2 与 Session 的结合使用
Cookie 与 Session 原理详解
一、Cookie 简介
1.1 Cookie 是什么?
- Cookie 是客户端(浏览器)用于存储少量数据的一种机制。
- 数据以
name=value
的形式保存在浏览器中。 - 每次向服务器发送请求时,符合条件的 Cookie 会自动随请求一起发送给服务器。
1.2 Cookie 的生成与存储位置
- 生成方式:
- 由服务器通过 HTTP 响应头中的
Set-Cookie
字段设置。 - 浏览器接收到后解析并存储。
- 由服务器通过 HTTP 响应头中的
- 存储位置:
- 运行内存(临时 Cookie):
- 关闭浏览器即失效。
- 未设置过期时间时默认如此。
- 硬盘文件(持久化 Cookie):
- 设置了过期时间后,浏览器将其写入磁盘。
- 即使关闭浏览器也不会丢失。
- 运行内存(临时 Cookie):
1.3 Cookie 的用途
- 维持用户状态: 在无状态的 HTTP 协议中,通过 Cookie 实现用户登录状态、购物车信息等的保持。
- 记录用户偏好: 如语言选择、主题风格等。
- 跨域通信辅助: 配合其他机制实现跨域单点登录(SSO)等功能。
二、Session 的原理及与 Cookie 的关系
2.1 Session 的实现原理
- 每个用户的会话信息被服务器端封装为一个
session
对象。 - 每个
session
对象都有一个唯一标识符:session ID。- 示例:
JSESSIONID=41C481F0224664BDB28E95081D23D5B8
- 示例:
- 这个 session ID 通常通过 Cookie 发送给浏览器,称为 会话 Cookie。
- 浏览器在后续请求中将该 Cookie 自动回传给服务器,从而实现会话跟踪。
✅ 注意:
- Session 是服务器端机制,而 Cookie 是客户端机制。
- Session 的 session ID 依赖于 Cookie 来实现“记忆”功能。
- 如果浏览器禁用了 Cookie,则需要 URL 重写来传递 session ID。
三、Cookie 的使用场景与经典案例
3.1 购物车功能(如京东商城)
- 用户未登录状态下添加商品到购物车。
- 即使关闭浏览器,再次访问网站时购物车内容仍存在。
- 实现原理:
- 将商品编号(如
productIds=1001,1002,1003
)存入 Cookie。 - Cookie 设置为持久化(保存在硬盘),有效期较长。
- 下次访问时读取 Cookie,还原购物车内容。
- 将商品编号(如
3.2 十天内免登录(如 126 邮箱)
- 用户勾选“十天内免登录”,登录成功后:
- 服务器生成包含用户名、密码等信息的 Cookie。
- Cookie 存储在硬盘上,有效期为 10 天。
- 用户下次访问时,浏览器自动发送该 Cookie。
- 服务器验证信息后完成自动登录。
🚫 Cookie 失效条件:
- 10 天后自动过期
- 用户修改密码
- 用户清除浏览器 Cookie
四、Java Web 中对 Cookie 的支持
4.1 Java Servlet 提供的类与方法
- 类:
jakarta.servlet.http.Cookie
- 创建 Cookie:
1
Cookie cookie = new Cookie("username", "zhangsan");
- 设置最大生存时间(单位:秒):
1
cookie.setMaxAge(60 * 60 * 24 * 10); // 10天
- 设置路径(path):
1
cookie.setPath("/myapp"); // 表示该路径及其子路径下有效
- 添加到响应对象中:
1
response.addCookie(cookie);
4.2 获取 Cookie
|
|
五、Cookie 的生命周期与作用范围
5.1 生命周期控制
设置值 | 效果 |
---|---|
未设置 setMaxAge() |
默认存储在运行内存中,浏览器关闭即失效 |
setMaxAge(0) |
删除同名 Cookie |
setMaxAge(n > 0) |
持久化保存,n 秒后失效 |
setMaxAge(n < 0) |
同未设置,存储在运行内存中 |
5.2 Cookie 的作用路径(Path)
- 默认路径:
- 与当前请求路径相同目录及其子路径有效。
- 自定义路径:
- 例如:
cookie.setPath("/project")
表示/project
及其子路径都可访问该 Cookie。
- 例如:
六、实战:用 Cookie 实现“十天内免登录”
6.1 功能流程
- 用户访问登录页面,输入用户名、密码,并选择“十天内免登录”选项。
- 登录成功后,服务器判断是否勾选免登录:
- 若勾选,则创建包含用户名、密码的 Cookie,并设置有效期为 10 天。
- 用户再次访问首页时,检查是否存在对应 Cookie:
- 若存在且合法,则跳转至登录后的页面;
- 否则,跳转至登录页。
6.2 核心代码片段
登录处理逻辑(Servlet)
|
|
页面自动登录检测逻辑(Filter 或 JSP)
|
|
七、总结与扩展思考
7.1 总结
特性 | Cookie | Session |
---|---|---|
存储位置 | 客户端(浏览器) | 服务端 |
安全性 | 相对较低(可能被篡改) | 较高(服务器管理) |
生命周期 | 可控(持久化或临时) | 依赖 Cookie 或 URL 重写 |
用途 | 记录用户信息、偏好设置等 | 保存敏感会话数据 |
7.2 扩展建议
- 安全性增强:
- 不应在 Cookie 中明文存储密码,应加密或使用 Token 替代。
- 现代替代方案:
- 使用 JWT(JSON Web Token)代替传统 Cookie + Session 机制。
- 使用 localStorage / sessionStorage 配合前端框架(如 Vue/React)实现本地状态管理。
- 移动端适配:
- 移动 App 中通常使用 SharedPreferences / Keychain / Secure Storage 替代 Cookie。
✨ JSP(JavaServer Pages)详解
一、初识 JSP
1. 第一个 JSP 程序
- 在 Web 应用的
WEB-INF
目录之外创建一个index.jsp
文件。 - 部署项目后启动服务器,在浏览器中访问:
1
http://localhost:8080/jsp/index.jsp
- 页面显示为空白,但底层执行的是:
index_jsp.class
这个 Java 类。
🔍 原理说明:
- Tomcat 会将
index.jsp
翻译为index_jsp.java
- 再将其 编译为
index_jsp.class
- 访问该页面时,实际上是调用了这个类的
service()
方法。
二、JSP 的本质与生命周期
1. JSP 是 Servlet 吗?
- 是的。JSP 本质上就是一个 Servlet!
- JSP 被翻译成 Java 类后,继承自
HttpJspBase
,而HttpJspBase
又继承自HttpServlet
。 - 所以,
index_jsp
类是一个标准的 Servlet。
2. 生命周期
- JSP 的生命周期与 Servlet 完全一致:
jspInit()
jspService()
jspDestroy()
3. 单例特性
- JSP 和 Servlet 一样,都是单例的(严格来说是“伪单例”),即每个请求共享同一个实例。
- 因此,不建议在 JSP 中使用成员变量或静态变量,避免线程安全问题。
三、JSP 的运行机制与性能优化
1. 为什么第一次访问 JSP 比较慢?
- 第一次访问需要经历以下过程:
- JSP 被翻译成
.java
文件 - 编译为
.class
字节码 - 创建 Servlet 实例
- 调用
init()
初始化 - 最后才调用
service()
方法处理请求
- JSP 被翻译成
2. 为什么第二次访问就快了?
- 因为已经生成
.class
文件并创建了 Servlet 实例。 - 后续请求直接复用该实例,仅调用
service()
方法即可。
🧩 运维建议:
在正式演示前,提前访问所有 JSP 页面,完成预热,提升用户体验。
四、JSP 的基础语法
1. JSP 是什么?
- JSP 是一种 动态网页技术,基于 Java 实现。
- 全称:JavaServer Pages
- 是 Java EE 规范的一部分。
- 所有 Web 容器(如 Tomcat)都内置 JSP 引擎,负责翻译和执行。
2. JSP 基础语法结构
语法 | 说明 |
---|---|
<% java代码 %> |
插入 Java 代码,翻译到 service() 方法内部 |
<%= 表达式 %> |
输出表达式的值,翻译为 out.print() |
<%! java代码 %> |
插入 Java 代码,翻译到 service() 方法外部(不推荐使用) |
<%-- 注释 --%> |
JSP 注释,不会出现在生成的 Java 代码中 |
<!-- HTML 注释 --> |
HTML 注释,会被翻译进 Java 代码中 |
3. page 指令
用于控制 JSP 的全局行为:
|
|
常用属性:
属性 | 说明 |
---|---|
contentType |
设置响应内容类型及字符集 |
pageEncoding |
设置当前 JSP 文件的编码格式 |
import |
导包 |
session |
是否启用 session 对象,默认为 true |
errorPage |
指定错误页面 |
isErrorPage |
当前页面是否作为错误页面使用 |
五、JSP 与 Servlet 的区别与协作
功能 | Servlet | JSP |
---|---|---|
主要职责 | 数据收集、业务逻辑处理 | 数据展示、页面渲染 |
优点 | 控制能力强、适合处理复杂逻辑 | 易于编写 HTML、适合前端展示 |
缺点 | 不适合写大量 HTML | 不适合处理复杂业务逻辑 |
✅ 最佳实践:
- 使用 Servlet 处理业务逻辑、连接数据库、获取数据
- 使用 JSP 展示数据、渲染页面
- 两者配合,发挥各自优势,提高开发效率和系统可维护性。
六、JSP 的作用域对象
JSP 支持四种作用域对象,按作用范围从小到大排列如下:
作用域 | 类型 | 生命周期 |
---|---|---|
pageContext |
PageContext |
页面内有效 |
request |
HttpServletRequest |
请求范围内有效 |
session |
HttpSession |
用户会话期间有效 |
application |
ServletContext |
整个应用生命周期内有效 |
📌 使用原则:
- 尽量使用最小的作用域,减少资源占用。
- 避免滥用 session 或 application 存储大量数据。
七、JSP 的九大内置对象
对象名 | 类型 | 用途 |
---|---|---|
pageContext |
PageContext |
页面上下文,管理其他作用域 |
request |
HttpServletRequest |
获取请求信息 |
response |
HttpServletResponse |
设置响应内容 |
session |
HttpSession |
管理会话状态 |
application |
ServletContext |
应用上下文 |
config |
ServletConfig |
获取配置信息 |
exception |
Throwable |
错误信息处理(需设置 isErrorPage=true ) |
page |
Object |
当前 JSP 对应的 Servlet 实例 |
out |
JspWriter |
向浏览器输出内容 |
八、常见问题与技巧
1. 如何调试 JSP?
- 查看对应的
.java
文件(位于work/Catalina/...
目录下) - 分析翻译后的 Java 代码,有助于理解执行流程和排查错误
2. JSP 文件扩展名可以修改吗?
- 可以通过修改
web.xml
文件中的<servlet-mapping>
来更改默认扩展名:
|
|
⚠️ 注意:修改后需重启服务器生效。
3. JSP 可以单独开发 Web 应用吗?
- 可以,因为 JSP 本质就是 Servlet,可以在
<% %>
中写 JDBC、业务逻辑等。 - 但不推荐,会导致代码混乱、难以维护。
九、实战案例:登录功能实现
1. 需求背景
- 系统需要登录验证,防止未授权用户访问敏感操作。
2. 实现步骤
-
创建用户表
t_user
1 2 3 4 5
CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50), password VARCHAR(50) );
-
创建登录页面
login.jsp
-
创建登录处理 Servlet
- 成功跳转至部门列表页
- 失败跳转至失败提示页
-
添加拦截机制(后续章节重点)
十、JSP 指令详解
1. 指令的作用
指导 JSP 引擎如何翻译当前文件。
2. 常见指令
指令 | 说明 |
---|---|
<%@ include file="xxx.jsp" %> |
静态包含另一个 JSP 文件 |
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> |
引入标签库(如 JSTL) |
<%@ page ... %> |
页面相关配置(最常用) |
十一、JSP 开发建议
建议 | 说明 |
---|---|
尽量少写 Java 代码 | JSP 更适合做视图层,逻辑应由 Servlet 或后端服务处理 |
统一编码格式 | 使用 pageEncoding 和 contentType 统一设置编码,避免乱码 |
合理使用作用域 | 根据需求选择合适的存储空间,避免内存浪费 |
分清职责边界 | JSP 负责展示,Servlet 负责处理,保持代码整洁 |
调试时查看翻译后的 Java 文件 | 有助于理解执行流程,排查问题 |
十二、拓展知识:JavaBean
什么是 JavaBean?
- 符合一定规范的 Java 类,常用于封装数据。
- 特征包括:
- 提供无参构造方法
- 属性私有化
- 提供公开的 getter/setter 方法
- 实现
Serializable
接口 - 可重写
toString()
、hashCode()
、equals()
方法
📦 JavaBean 是实体类的标准形式,广泛应用于 MVC 架构的数据传递。
总结
JSP 是 Java Web 开发中非常重要的组成部分,虽然现在被 Thymeleaf、Freemarker 等模板引擎部分替代,但在传统项目中仍广泛应用。掌握其工作原理、语法结构、与 Servlet 的协同关系,是构建高质量 Web 应用的基础。
EL 表达式详解
一、什么是 EL 表达式?
- 全称:Expression Language(表达式语言)
- 作用:
- 替代 JSP 中的 Java 脚本代码(如
<% %>
、<%= %>
),使页面更加整洁、易于维护。 - 是 JSP 技术的一部分,主要用于简化从作用域中获取数据并输出到浏览器的过程。
- 替代 JSP 中的 Java 脚本代码(如
- 出现背景:
- 在早期 JSP 页面中夹杂大量 Java 代码,导致页面混乱,难以阅读与维护。
- 为了解决这个问题,引入了 EL 表达式。
二、EL 表达式的核心功能
EL 表达式主要有三大核心功能:
-
从某个作用域中取数据
- 支持四个作用域(范围从小到大):
pageContext
(当前页面)request
(一次请求)session
(一次会话)application
(整个应用)
- 取值时,优先从最小的作用域中查找。
- 支持四个作用域(范围从小到大):
-
将取出的数据转换为字符串
- 如果是对象,自动调用其
toString()
方法进行转换。
- 如果是对象,自动调用其
-
将字符串输出到浏览器
- 等同于
<%= %>
的效果。
- 等同于
三、基本语法格式
|
|
示例:
|
|
等价于:
|
|
四、使用场景与实例解析
4.1 数据存入作用域
在 JSP 中,必须先将数据放入某个作用域中,EL 才能访问:
|
|
4.2 使用 EL 获取数据
|
|
⚠️ 注意:如果没有对应的
getXxx()
方法,会抛出异常(500 错误)。
4.3 面试题解析
${abc}
vs ${"abc"}
表达式 | 含义 |
---|---|
${abc} |
从作用域中取出名为 "abc" 的属性值 |
${"abc"} |
直接输出字符串 "abc" ,不从作用域中取值 |
五、EL 表达式的隐式对象
EL 提供了一些常用的内置对象,方便开发者直接访问上下文信息:
对象名 | 说明 |
---|---|
pageContext |
当前页面上下文对象 |
param |
获取请求参数(单个值) |
paramValues |
获取请求参数(数组形式) |
initParam |
获取 web.xml 中的初始化参数 |
pageScope |
访问 page 作用域中的数据 |
requestScope |
访问 request 作用域中的数据 |
sessionScope |
访问 session 作用域中的数据 |
applicationScope |
访问 application 作用域中的数据 |
六、EL 表达式对特殊字符的支持
如果 key 名中包含特殊字符(如 .
),建议使用 [ ]
操作符:
|
|
错误写法:
|
|
正确写法:
|
|
七、EL 表达式操作集合与数组
7.1 Map 集合
|
|
7.2 数组 / List 集合
|
|
八、控制 EL 表达式是否启用
可以通过 JSP 的 page
指令全局控制 EL 表达式是否启用:
|
|
isELIgnored="true"
:忽略所有 EL 表达式isELIgnored="false"
:启用 EL 表达式(默认)
也可以通过 \
符号局部禁用 EL:
|
|
九、常用 EL 表达式技巧
9.1 获取项目根路径
|
|
等价于:
|
|
9.2 运算符支持
类型 | 示例 |
---|---|
算术运算 | +, -, *, /, % |
关系运算 | == (eq), !=, >, >=, <, <= |
逻辑运算 | not, and, or |
条件运算 | ${a ? b : c} |
判空运算 | empty / not empty |
示例:
|
|
十、EL 表达式的优势与适用场景
优势 | 说明 |
---|---|
简洁易读 | 替代复杂 Java 脚本,提升可读性 |
自动类型转换 | 自动调用 toString() 方法 |
支持嵌套对象访问 | 如 ${user.address.zipCode} |
安全处理 null | null 被转换为空字符串,避免报错 |
支持多种集合类型 | Map、List、Array 等 |
✅ 推荐使用场景:JSP 页面中展示数据时,替代
<%= %>
和<% %>
,保持页面干净整洁。
十一、总结
EL 表达式是 JSP 开发中非常重要的工具,能够显著提高页面的可读性和可维护性。掌握其基本语法、作用域机制、隐式对象及常见操作,有助于构建更优雅的 Web 应用程序。
如果你正在开发传统的 JSP 项目,强烈建议多使用 EL 表达式,减少 Java 脚本的使用。同时,也可以配合 JSTL 标签库一起使用,实现更强大的页面逻辑控制。
如需进一步学习,推荐搭配 JSTL 核心标签库 一起使用,提升开发效率。
JSTL 标签库(Java Standard Tag Library)
JSTL 是 Java Web 开发中用于简化 JSP 页面逻辑处理的标准标签库。它与 EL 表达式配合使用,旨在减少或替代 JSP 中的 Java 脚本代码,提升代码可读性和维护性。
一、什么是 JSTL 标签库?
- 全称: Java Standard Tag Lib
- 作用:
- 替代 JSP 中
<% %>
的 Java 代码。 - 提供结构化、语义化的标签语法。
- 支持条件判断、循环、变量赋值等常见逻辑控制。
- 替代 JSP 中
- 实现原理:
- JSTL 标签在 JSP 中被解析为对应的 Java 类(封装在 jar 包中)。
- 实际上,每个标签背后都对应一个实现了特定功能的 Java 类。
💡 小贴士:
虽然 JSTL 可以简化页面逻辑,但复杂的业务逻辑仍建议放在后端控制器中处理,保持视图层简洁。
二、使用 JSTL 标签库的步骤
1. 引入 JSTL 相关依赖包
-
Tomcat 10+ 所需的 jar 包:
jakarta.servlet.jsp.jstl-api-2.0.0.jar
(接口)jakarta.servlet.jsp.jstl-2.0.0.jar
(具体实现类)
-
IDEA 配置方法:
- 将上述两个 jar 文件复制到项目的
/WEB-INF/lib/
目录下。 - 右键 →
Add as Library...
添加为项目库。 - 确保部署时这些 jar 包会被打包进 WAR 文件。
- 将上述两个 jar 文件复制到项目的
⚠️ 注意:所有需要随项目部署的 jar 包都应放入
/WEB-INF/lib
目录中,这样 Tomcat 在运行时才能正确加载它们。
2. 在 JSP 页面中引入标签库
使用 <%@ taglib %>
指令引入 JSTL 标签库,例如核心标签库:
|
|
prefix="c"
:给标签库起个别名,通常用c
表示 core。uri
:指定标签库的唯一标识符,指向对应的.tld
配置文件。
3. 使用 JSTL 标签
在 JSP 页面中使用类似如下语法调用标签:
|
|
表面上是标签,实际上底层会调用相应的 Java 类来执行逻辑。
三、JSTL 标签的工作原理
1. TLD 文件的作用
-
TLD(Tag Library Descriptor)是一个 XML 文件,描述了标签库的元信息。
-
它定义了:
- 标签名称
- 对应的 Java 类
- 标签体内容类型
- 属性及其特性(是否必需、是否支持 EL)
-
位置:
- 在
jakarta.servlet.jsp.jstl-2.0.0.jar
的META-INF/c.tld
文件中。
- 在
2. 示例 TLD 片段解析
|
|
tag-class
:标签对应的 Java 类。body-content
:标签体内允许的内容类型。attribute
:定义标签属性行为。
四、JSTL 核心标签库常用标签详解
以下是 JSTL Core 标签库中最常用的几个标签及其用法说明:
1. <c:if>
用于条件判断,相当于 Java 中的 if
。
|
|
test
属性接受布尔表达式,支持 EL 表达式。
2. <c:forEach>
用于遍历集合或进行计数循环。
遍历集合:
|
|
items
:要遍历的数据源(如 List、Map)。var
:每次迭代的元素变量名。varStatus
(可选):提供索引、计数等状态信息。
计数循环:
|
|
begin
、end
、step
控制循环范围和步长。
3. <c:choose>
, <c:when>
, <c:otherwise>
组合使用实现多条件分支判断,类似于 Java 的 switch-case
或多个 if-else
。
|
|
<c:choose>
是外层容器。<c:when>
是条件分支。<c:otherwise>
是默认情况(可选)。
五、补充建议与最佳实践
-
避免过度使用 JSTL:
- JSTL 不适合处理复杂业务逻辑,建议将数据处理放在 Controller 层,JSP 只负责展示。
-
EL 表达式搭配使用:
- JSTL 和 EL 表达式是“黄金搭档”,能有效减少脚本嵌套,提高可读性。
-
版本兼容性注意:
- JSTL 1.x 适用于 Servlet 2.x / JSP 2.x。
- JSTL 2.x 适用于 Servlet 3.x / JSP 2.3,推荐用于现代项目。
-
推荐使用 Spring MVC + Thymeleaf:
- 如果你正在开发新项目,考虑使用 Spring Boot + Thymeleaf 模板引擎,其功能更强大且更现代化。
六、总结
内容 | 说明 |
---|---|
目标 | 替代 JSP 中的 Java 脚本代码,提升可读性 |
核心组件 | JSTL Core 标签库(c:if , c:forEach , c:choose 等) |
关键配置 | 引入 JAR 包、在 JSP 中使用 <%@ taglib %> 声明 |
底层机制 | TLD 描述标签与 Java 类之间的映射关系 |
最佳实践 | 仅用于视图逻辑,避免复杂运算;推荐结合 EL 使用 |
改造OA
-
使用什么技术改造呢?
- Servlet + JSP + EL表达式 + JSTL标签。进行改造。
-
在前端HTML代码中,有一个标签,叫做base标签,这个标签可以设置整个网页的基础路径。
-
这是Java的语法,也不是JSP的语法。是HTML中的一个语法。HTML中的一个标签。通常出现在head标签中。
-
< base href=“http://localhost:8080/oa/">
-
在当前页面中,凡是路径没有以“/”开始的,都会自动将base中的路径添加到这些路径之前。
- < a href=“ab/def”></ a>
- 等同于:< a href=“http://localhost:8080/oa/ab/def”></ a>
-
需要注意:在JS代码中的路径,保险起见,最好不要依赖base标签。JS代码中的路径最好写上全路径。
-
1
<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
-
Filter 过滤器详解
一、问题背景:OA项目中重复代码的问题
在 OA 项目开发中,我们通常会遇到多个 Servlet(如 DeptServlet
、EmpServlet
、OrderServlet
)都需要执行一些公共操作的情况。例如:
- 用户登录验证:每个 Servlet 执行前都要判断用户是否已登录。
- 中文乱码处理:每次请求都需要设置编码格式。
这些问题导致了大量重复代码,降低了代码复用性和可维护性。
解决方案:
使用 Servlet 规范中的 Filter(过滤器)机制 来集中处理这些公共逻辑。
二、Filter 是什么?有什么作用?
1. 定义
Filter(过滤器)是 Servlet 规范的一部分,用于在请求到达目标 Servlet 前或响应返回客户端前进行预处理或后处理。
2. 主要用途
- 统一处理请求(如权限校验、日志记录)
- 统一处理响应(如压缩输出、添加响应头)
- 避免代码冗余,提高模块化和可维护性
3. 执行原理
Filter 的执行流程如下:
|
|
Filter 可以在请求处理的前后插入逻辑,类似于 AOP(面向切面编程)的思想。
三、如何编写一个 Filter?
步骤一:创建 Filter 类
实现 javax.servlet.Filter
接口,并重写三个核心方法:
|
|
步骤二:配置 Filter
方式一:web.xml 配置
|
|
方式二:注解方式(推荐)
|
|
四、Filter 的生命周期
Filter 生命周期与 Servlet 类似,但有以下区别:
对象类型 | 默认实例化时间 | 是否单例 |
---|---|---|
Filter | 服务器启动时自动实例化 | ✅ 是 |
Servlet | 第一次请求时才实例化 | ✅ 是 |
五、Filter 的匹配规则
Filter 的 URL 匹配方式非常灵活,常见有以下几种:
匹配方式 | 示例 | 说明 |
---|---|---|
精确匹配 | /login.do |
仅匹配指定路径 |
后缀匹配 | *.do |
匹配所有以 .do 结尾的请求 |
路径前缀匹配 | /dept/* |
匹配 /dept/add 、/dept/edit 等 |
通配符匹配 | /* |
匹配所有请求 |
六、Filter 的执行顺序
Filter 的执行顺序取决于配置方式:
1. web.xml 中的顺序
Filter 的执行顺序与 <filter-mapping>
标签的书写顺序有关,越靠上优先级越高。
示例:
|
|
执行顺序为:A → B → Servlet → B → A
2. 使用 @WebFilter 注解
Filter 的执行顺序默认按照类名排序决定:
FilterA
会比FilterB
更早执行。Filter1
会比Filter2
更早执行。
七、责任链设计模式(Chain of Responsibility Pattern)
Filter 的最大优势在于其支持 责任链设计模式,即:
- 在运行阶段动态组合 Filter 的执行顺序。
- 不依赖编译时的硬编码,而是通过配置文件控制流程。
核心思想:
将多个对象连接成一条链,请求在这条链上传递,直到有一个对象处理它为止。
应用场景:
- 登录验证 → 日志记录 → 编码处理
- 多层权限拦截
- 请求缓存控制
八、Filter 和 Servlet 的关系
特点 | Filter | Servlet |
---|---|---|
是否自动初始化 | ✅ 是 | ❌ 否(首次请求时初始化) |
是否单例 | ✅ 是 | ✅ 是 |
是否处理请求 | ✅ 是(前置/后置) | ✅ 是(处理业务逻辑) |
是否决定执行下一级 | ✅ 是(通过 chain.doFilter() ) |
❌ 否 |
九、实战应用:使用 Filter 改造 OA 项目
场景需求:
- 所有以
.do
结尾的请求都必须先登录。 - 所有请求都需统一设置编码。
实现步骤:
1. 创建登录验证 Filter
|
|
2. 创建编码处理 Filter
|
|
3. 效果
- 用户未登录时,会被跳转到登录页面。
- 所有请求无需手动设置编码,避免中文乱码。
十、总结与建议
优点总结:
- 减少重复代码,提高代码复用率
- 提升系统的可维护性和扩展性
- 支持责任链设计模式,灵活控制执行流程
使用建议:
- 将公共逻辑提取到 Filter 中(如登录验证、编码设置、日志记录等)
- 合理配置 Filter 的匹配路径,避免影响非目标资源(如静态资源)
- 注意 Filter 的执行顺序,合理安排逻辑顺序
Listener 监听器(监听器机制)
一、什么是监听器?
监听器是 Servlet 规范 中提供的一种回调机制,用于在特定事件发生时自动执行预定义的代码逻辑。
- 类似于 Filter 过滤器,都是 Servlet 规范的一部分。
- 所有监听器接口名称都以
Listener
结尾,例如ServletContextListener
、HttpSessionListener
等。
二、监听器的作用
监听器允许我们在 Web 应用生命周期中的关键节点 或 域对象状态变化时 插入自定义逻辑。
- 举例:
- 应用启动或关闭时初始化资源或释放资源;
- 用户登录/退出时更新在线人数;
- Session 创建或销毁时记录日志等。
三、Servlet 规范中常见的监听器分类
1. jakarta.servlet
包下的通用监听器
监听器名称 | 功能描述 |
---|---|
ServletContextListener |
监听整个 Web 应用的上下文(ServletContext )的创建与销毁 |
ServletContextAttributeListener |
监听 ServletContext 域中属性的添加、替换、移除 |
ServletRequestListener |
监听每次请求的创建与销毁 |
ServletRequestAttributeListener |
监听 ServletRequest 域中属性的变化 |
2. jakarta.servlet.http
包下的 HTTP 专用监听器
监听器名称 | 功能描述 |
---|---|
HttpSessionListener |
监听 HttpSession 的创建与销毁 |
HttpSessionAttributeListener |
监听 HttpSession 域中属性的添加、替换、移除 |
HttpSessionBindingListener |
对象绑定或解绑到 session 时触发(无需配置) |
HttpSessionIdListener |
session ID 改变时触发 |
HttpSessionActivationListener |
监听 session 的钝化(序列化到磁盘)与活化(从磁盘恢复) |
✅ 小贴士:
HttpSessionBindingListener
是唯一一个不需要使用@WebListener
注解或在web.xml
中注册的监听器。- 只要某个类实现了该接口,当它被加入或移出 session 时,会自动触发相应方法。
四、实现监听器的基本步骤(以 ServletContextListener
为例)
步骤一:编写监听器类
|
|
步骤二:注册监听器(两种方式)
方式一:使用 @WebListener
注解(推荐)
只需在类上加上 @WebListener
即可自动注册。
方式二:在 web.xml
中手动配置
|
|
五、监听器方法的调用时机
所有监听器中的方法都不是由程序员主动调用的,而是由 Web 容器(如 Tomcat)根据事件自动触发的。
- 例如:
- 当 Web 应用启动时,自动调用
contextInitialized()
; - 当用户访问页面时,自动调用
requestInitialized()
; - 当 session 被创建或销毁时,自动调用
sessionCreated()
或sessionDestroyed()
。
- 当 Web 应用启动时,自动调用
六、典型业务场景分析
场景一:统计当前网站的在线用户数
思路:
- 每当一个新的
HttpSession
被创建时,表示有一个新用户访问; - 每当一个
HttpSession
被销毁时,表示用户离开; - 利用
HttpSessionListener
实现计数器逻辑。
|
|
📌 注意:此方案统计的是所有会话,包括未登录用户。
场景二:仅统计已登录用户的在线数量
问题背景:
- 需要区分“访客”和“登录用户”;
- 登录标志通常是将用户信息存入 session,如
session.setAttribute("user", userObj)
; - 用户登出则是
session.removeAttribute("user")
或 session 超时。
解决方案:
使用 HttpSessionBindingListener
接口,让 User 类实现该接口:
|
|
✅ 优势:
- 更加精确地统计“登录用户”的在线情况;
- 不依赖 session 的创建/销毁,只关注用户是否真正登录。
七、实际项目案例:OA 系统中统计登录用户数
1. 登录标识判断
-
用户登录成功后,通常会执行如下代码:
1
session.setAttribute("user", user); // 表示用户已登录
-
因此可以利用
HttpSessionBindingListener
来监听user
对象的绑定和解绑。
2. 用户退出标识
-
用户登出时执行:
1
session.removeAttribute("user"); // 用户主动登出
-
或者 session 超时自动销毁,也会触发
valueUnbound()
方法。
八、总结
功能 | 使用的监听器 |
---|---|
应用启动/关闭 | ServletContextListener |
请求开始/结束 | ServletRequestListener |
Session 创建/销毁 | HttpSessionListener |
Session 属性变化 | HttpSessionAttributeListener |
用户登录/退出(精准) | HttpSessionBindingListener |
Session ID 变化 | HttpSessionIdListener |
Session 钝化/活化 | HttpSessionActivationListener |
九、最佳实践建议
- 合理选择监听器类型:根据需求选择最合适的监听器,避免过度监听。
- 注意线程安全问题:多个用户并发访问时,共享变量(如在线人数)需考虑同步控制。
- 不要在监听器中执行耗时操作:如数据库查询、网络请求等,会影响系统性能。
- 使用注解简化配置:优先使用
@WebListener
注解而非 XML 配置。