java-Web基础之Servlet、Filter、Listener
努力有时候也战胜不了天分,但至少能让别人看得起你。
——《七龙珠》
手动创建一个Maven项目
由于要学习Java中的Servlet,Filter,Listener等组件需要下载依赖,所以我们需要在构建工具中进行演示和学习。本文使用Maven项目作为示例。
创建文件夹结构
1 |
|
手动创建一个简单的 pom.xml
1 |
|
这样我们就创建好了一个最简单maven项目。
Servlet
Servlet 是JavaWeb技术的核心组件,本质上就是运行在服务器端的小程序,用于处理客户端(如浏览器)的请求,生成动态响应。Servlet主要用于处理Http请求,实现动态网页内容生成。
Servlet 生命周期
Servlet的生命周期由Servlet容器(如Tomcat)管理,主要有以下几个阶段:
Servlet容器:Web服务器,用于接收HTTP请求并调用你写的Servlet 代码处理请求。
初始化 init()
–只调用一次
- 在Servlet第一次被访问时,容器会创建一个Servlet实例,然后调用它的
init()
方法。 - 通常用来做资源的初始化操作、比如数据库连接、读取配置文件等。
1 |
|
注:只有一个实例,init()只调用一次
请求处理 service()
–每次请求都会调用
- 每次有客户端请求Servlet,Tomcat就会调用Servlet实例的
service()
方法 service()
实例会自动判断请求类型(GET/POST),然后分发给对应的doGet() 或 doPost()
1 |
|
注:此处我们先暂时了解大概流程,无需关心代码实际实现
销毁 destroy()
–只调用一次
- 当Tomcat关闭、或Servlet被卸载(如热更新时),容器会调用
destroy()
方法 - 通常用来释放资源,如关闭数据库连接,停止线程等。
1 |
|
补充说明
- 一个Servlet类只被实例化一次(单例),多个请求公用一个实例,线程不安全。
- Servlet生命周期由容器自动管理,你无需手动调用这些方法
- SpringMVC的低层DispatchServlet也是一个Servlet,生命周期和你写的一样。
部署Tomcat
Apache 基金会开发的免费、开源、轻量级 Java Web 容器 ,支持 Servlet 和 JSP 规范
- SpringBoot + SpringMVC 中内置Tomcat,无需配置容器,直接Main方法运行。
首先我们在官网下载安装TomCat:Apache Tomcat® - Welcome!
安装后记住安装路径,后面要用。
然后我们在VScode中安装扩展:
这个扩展帮助我们管理和启动Tomcat。
安装完成后,我们在VScode的侧边栏中的SERVICE中找到这个扩展(注意在资源管理器栏的最下面):
我们右键这个服务选择:Creat New Server
现在我们查看VScode的最上面,询问我们是否需要下载server,由于我们之前已经手动下载过Tomcat,所以选择No。
然后我们在弹出的窗口中选择之前安装的Tomcat的文件夹,来到这个页面:
不用更改配置,直接滑倒最下面点击Finish即可。
现在我们看到扩展中已经托管了Tomcat:
现在我们创建项目结构如下:
1 |
|
pom.xml文件如下:
1 |
|
LifecycleServlet.java 文件内容如下:
1 |
|
然后我们在终端中输入:
1 |
|
好,现在我们只需要右键Tomcat选择:add Deployment
然后选择File:
选择我们刚刚编译好的wer文件:
最后选择No:
我们就将刚刚编写好的Servlet加入了Tomcat容器中。
右键Tomcat服务选择Start Server,服务开始运行。此时打印的信息只有Tomcat的启动信息,Servlet还没有被创建。
然后我们浏览器访问:http://localhost:8080/servlet-lifecycle-demo/life
看到servlet实例被创建,然后调用了一次GET请求,
之后再次访问页面,也不会在创建新的Servlet实例,而是一直调用同一个实例的doGet方法
最后我们Stop Server:
我们可以在日志中找到Servlet销毁函数调用是打印的日志。
Servlet Mapping
Servlet的作用是将客户端发送的请求URL路径与特定的Servlet 类关联起来。当接收到符合某个映射规则的请求时,它会将请求交给对应的Servlet来处理。
使用 @WebServlet
注解配置映射
在我们上面的代码中,我们使用
1 |
|
进行注解配置。这里的”/life” 就是一个URL模式(URL pattern), 当用户访问路径 /life
时,Tomcat就会将这个请求交给 LifecycleServlet
的实例来处理。
多个URL模式:可以为一个Servlet映射多个URL模式
1
@WebServlet({"/life","/mylife"})
这样当访问多个路径中的任意一个路径时,都会交给
LifecycleServlet
实例进行处理指定
name
:可以为Servlet指定一个名称1
2
3
4@WebServlet(name = "lifecycleServlet", urlPatterns = {"/life"})
public class LifecycleServlet extends HttpServlet {
// ... your code ...
}在注解映射时,name属性通常不是必须的,但是在web.xml中,servlet-name属性是必须的,所以这里介绍一下。
使用 web.xml
配置映射(传统方式)
在web应用的 WEB-INF
目录下,会有一个web.xml文件。我们也可以通过这个文件进行Servlet配置和Servlet映射。
1 |
|
<servlet>
用于声明一个Servlet,通过<servlet-name>
指定名称(必须),<servlet-class>
指定Servlet类的完整限定名。<servlet-mapping>
元素用于将一个Servlet名称<servlet-name>
映射到一个URL模式<url-pattern>
URL模式的类型
- 精确匹配:以
/
开头,例如/life
。只用当请求的路径与此路径完全匹配时,Servlet才会被调用。 - 路径匹配:以
/
开头,以/*
结尾,例如/admin/*
。任何以/admin/
开头的请求都会被匹配到这个Servlet。 - 扩展名匹配:以
*.
开头,例如*.do
。任何以.do
结尾的请求都会被匹配到这个Servlet - 默认Servlet:只有一个
/
作为URL模式,他会处理所有其他Servlet映射都无法匹配的请求。
Servlet特性
Servlet的线程安全性:
- Servlet实例是单例的,但是对于每个请求,Servlet容器都会创建一个新的线程来执行Servlet的
service()
方法(doGet()
或doPost()
) - Servlet需要考虑线程安全性。避免在Servlet的实例变量中存储共享的可变状态,或者采取同步措施来保护共享资源。
Servlet的配置(ServletConfig):
- ServletConfig 对象代表Servlet的配置信息,每个servlet都有自己的ServletConfig对象。
- 我们可以在
web.xml
(或使用@WebInitParam
注解)为Servlet配置初始化参数,并通过ServletConfig
的getInitParameter(String name)
方法获取。
一个小DEMO
- 使用ServletConfig获取初始化参数:
1 |
|
1 |
|
- 使用
@WebInitParam
注解
1 |
|
无论使用注解还是web.xml在Servlet类中获取初始化参数的方法都一样。只是注册参数的方法不同。
Servlet API详解
Servlet API 是构建Java Web 应用的核心。它定义了Servlet容器和你的Servlet之间的契约。
HttpServletRequest 接口
HttpServletRequest
对象代表了客户端发送给Servlet的HTTP请求。通过这个对象,你可以获取到请求的所有信息。
常用方法:
获取参数时,如果没有设置该参数,则会得到
null
getParameter(String name)
: 获取指定名称的请求参数的值(返回String
)。如果参数不存在或有多个同名参数,行为可能不确定。getParameterValues(String name)
: 获取指定名称的所有请求参数的值(返回String[]
)。getParameterMap()
: 获取所有请求参数的Map<String, String[]>
。getHeader(String name)
: 获取指定名称的请求头的值(返回String
)。getHeaders(String name)
: 获取指定名称的所有请求头的值(返回Enumeration<String>
)。getHeaderNames()
: 获取所有请求头的名称(返回Enumeration<String>
)。getMethod()
: 获取请求的 HTTP 方法(例如:”GET”, “POST”)。getRequestURI()
: 获取请求的 URI。getContextPath()
: 获取 Web 应用的上下文路径。getServletPath()
: 获取 Servlet 的路径。getQueryString()
: 获取查询字符串(URL 中?
后面的部分)。getRemoteAddr()
: 获取发送请求的客户端的 IP 地址。getRemoteHost()
: 获取发送请求的客户端的主机名。getSession()
: 返回当前请求关联的HttpSession
,如果不存在则创建一个新的。getSession(boolean create)
: 返回当前请求关联的HttpSession
,如果create
为true
且不存在则创建一个新的,否则返回null
。setAttribute()
:HttpServletRequest
的属性只在当前请求的处理过程中有效,当该请求的响应发送回客户端后,这些属性就不再需要了getAttribute()
:获取设置的属性。
练习1:获取get请求中的参数
name=ulna&age=20
1 |
|
HttpServletResponse 接口
HttpServletResponse
对象代表了Servlet发送给客户端的Http响应。通过这个对象,我们可以设置响应的内容、状态码、响应头等信息。
常用方法:
getWriter()
: 返回一个PrintWriter
对象,你可以使用它向客户端发送文本数据。getOutputStream()
: 返回一个ServletOutputStream
对象,你可以使用它向客户端发送二进制数据(例如:图片、文件)。setContentType(String type)
: 设置响应的内容类型(例如:”text/html”, “application/json”)。同时也可以设置字符编码,例如"text/html;charset=UTF-8"
。setStatus(int sc)
: 设置响应的状态码(例如:200, 404, 500)。sendError(int sc, String msg)
: 发送一个错误响应到客户端,并设置状态码和错误消息。sendRedirect(String location)
: 发送一个临时的重定向响应,告诉客户端浏览器去访问另一个 URL。setHeader(String name, String value)
: 设置指定的响应头。addHeader(String name, String value)
: 添加指定的响应头(允许有多个同名 header)。setDateHeader(String name, long date)
: 设置日期格式的响应头。setIntHeader(String name, int value)
: 设置整数格式的响应头。setContentLength(int len)
: 设置响应内容的长度。
练习2:创建一个Servlet,当通过访问特定的URL时如:
/redirect-me
, 他会使用sendRedirect() 方法将客户端重定向到另一个你指定的URL例如:https://www.google.com
1 |
|
ServletConfig 接口
每个Servlet都有一个对应的 ServletConfig
对象,Servlet 容器在构建Servlet时会创建这个对象,并传递给Servlet的 init(ServletConfig config)
方法。ServletConfig
接口允许你访问Servlet的配置信息。
注册初始参数:
只有一个参数时:通常可以这样简化写
1 |
|
多个参数时:规范写法
1 |
|
常用方法:
getServletName()
: 返回 Servlet 的名称,通常是在web.xml
或使用@WebServlet
注解时指定的。getInitParameter(String name)
: 返回 Servlet 初始化参数中指定名称的值(返回String
)。这些参数可以在web.xml
中通过<init-param>
标签配置,或者在使用注解时通过@WebInitParam
配置。getInitParameterNames()
: 返回包含所有 Servlet 初始化参数名称的Enumeration<String>
.getServletContext()
: 返回当前 Web 应用的ServletContext
对象。
练习3:创建一个Servlet,通过
@WebInitPara
注解配置至少两个初始化参数。在Servlet的doGet()
方法中获取并显示这些初始化参数的值以及servlet的名称。
1 |
|
ServletContext 接口
ServletContext
接口代表了当前的Web应用程序。它是整个Web应用共享的上下文,所有的Servlet、Filter、Listener 等都可以访问同一个 ServletContext
对象。Servlet 容器在启动Web应用时创建 ServletContext
对象。
常用方法:
getAttribute(String name)
: 返回指定名称的应用程序级别的属性值(返回Object
)。setAttribute(String name, Object object)
: 设置应用程序级别的属性/对象。removeAttribute(String name)
: 移除指定名称的应用程序级别的属性。getInitParameter(String name)
: 返回 Web 应用的上下文初始化参数中指定名称的值(返回String
)。这些参数在web.xml
的<context-param>
标签中配置。getInitParameterNames()
: 返回包含所有 Web 应用上下文初始化参数名称的Enumeration<String>
.getResource(String path)
: 返回指定路径的资源 URL。路径以/
开头,相对于 Web 应用的根目录。getResourceAsStream(String path)
: 返回指定路径的资源输入流。getContextPath()
: 返回 Web 应用的上下文路径。getServletContextName()
: 返回 Web 应用的名称(如果在部署描述符中指定了)。log(String msg)
: 将消息写入 Servlet 容器的日志。
练习4:
- 在你的web应用的web.xml文件种配置至少一个
<context-param>
- 创建一个Servlet,该Servlet 获取并显示这个上下文参数的值。
- 在该Servlet中,设置一个ServletContext属性,并在同一个Servlet中获取并显示这个属性的值。
web.xml:
1 |
|
1 |
|
Servlet 请求转发与重定向
请求转发
请求转发是指将当前请求的处理交给web服务器上的另一个资源来完成,这个过程发生在服务器内部,客户端浏览器只发送一次请求(与重定向的一个关键不同)。
实现方式:
通过 RequestDispatcher
接口来实现请求转发。我们可以通过 HttpServletRequest
对象的 getRequestDispatcher(String path)
方法获取一个 RequestDispatcher
对象。path 可以是相对于当前Servlet上下文路径。
RequestDispatcher
接口提供了两个主要方法用于转发:
forword(ServletRequest request, ServletResponse response)
:将请求转发给另一个资源。- 注意:在转发后,
forword()
后的代码有可能会执行。但是在调用之后,当前Servlet不应该再向客户端发送任何响应。最佳实践是forwrod()
后就立即结束当前方法。
- 注意:在转发后,
include(ServletRequest request, ServletResponse response)
:将另一个资源的内容包含到当前响应中。执行流程会回到当前Servlet继续处理。
1 |
|
1 |
|
请求重定向
请求重定向是指服务器告诉客户端浏览器发送一个新的HTTP请求到另一个URL。这涉及到两次HTTP请求-响应周期。
实现方式:
通过 HttpServletResponse
对象的 sendRedirect(String location)
方法来发送重定向响应。
location
是客户端浏览器需要重新访问的URL。
1 |
|
Filter
Filter基础知识
Filter(过滤器)是Servlet规范中一个非常重要的组件,它的核心作用是在请求到达目标Servlet或JSP之前,以及响应离开目标Servlet/JSP后,对请求和响应进行截获和处理。
Fliter的作用:
在没有Filter之前,如果我们需要对所有请求进行一些通用操作(比如权限检查、编码设置),我们就在每个Servlet中重复编写这些逻辑。这回导致大量的重复代码,难以维护。Filter的出现就是为了解决这个问题,它允许我们将这些通用的、与具体业务逻辑无关的功能从Servlet中分离出来,集中处理。
应用场景:
- 权限认证:检查用户是否已经登录,是否有权访问某个资源。
- 日志记录:记录每个请求的详细信息,例如请求路径、用户IP、请求时间、响应耗时等。
- 字符编码处理:统一设置请求和响应的字符编码,防止中文乱码问题。
- 数据压缩:对响应内容进行GZIP压缩,减少网络传输量,提高加载速度。
- 敏感词过滤:对用户提交的数据进行过滤,例如评论中的敏感词。
- 缓存控制:设置HTTP响应头,控制客户端的缓存行为。
- 会话管理:检查会话是否存在,或在会话过期时重定向。
Filter的生命周期
Filter也有自己的生命周期,由Servlet容器如(Tomcat)管理:
加载和实例化:当Web应用程序启动或者容器首次需要使用某个Filter时,容器会加载并创建Filter的实例。
初始化(
init()
):容器会调用Filter实例的init()
方法。这个方法只被调用一次,用于执行一些初始化操作,比如读取配置参数、简历数据库连接等。方法签名:
1
void init(FilterConfig filterConfig) throws ServletException
请求处理(
doFilter()
):当客户端请匹配到该Filter拦截的URL模式时,容器就会调用Filter实例的doFilter()
方法。这是Filter执行核心逻辑的地方。
方法签名:1
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
销毁(
destroy()
):当Web应用程序关闭或者Filter被卸载时,容器会调用Filter实例的destroy()
方法。这个方法也只会调用一次,用于释放资源,比如关闭数据库连接、清理缓存等。方法签名:
1
void destroy()
Filter三大接口
javax.servlet.Filter
接口
javax.servlet.Filter
是所有自定义过滤器的基石。它是一个接口,定义了Filter必须实现的三个核心方法。
- init()
- doFilter()
- destroy()
1 |
|
javax.servlet.FilterChain
接口
FilterChain(过滤器链)是Filter机制中一个至关重要的概念。当一个请求进入一个Web应用程序时,如果它需要经过多个Filter的处理,这些Filter就会被组织成一个链条。FilterChain 就是这个链条的抽象。
FilterChain
接口只有一个核心方法:
1 |
|
在自定义Filter的doFilter()方法中,通常会使用到 chain.doFilter(request,response)
作用:
- 如果当前Filter不是链中的最后一个Filter,它会将请求和响应对象传递给下一个Filter。
- 如果当前Filter是链中的最后一个Filter,它会将请求和响应对象传递给最终的目标资源(例如Servlet或JSP)
如果没用调用 chain.doFilter(request, response)
,那么请求就不会继续传递下去,它将被中断,后续的Filter或目标资源将永远不会执行到。
javax.servlet.FilterConfig
接口
FilterConfig
对象提供了Filter的配置信息。当容器调用Filter的 init()
方法时,会将一个 FilterConfig
实例作为参数传递进来。
FilterConfig
接口提供的方法包括:
String getFilterName()
:获取Filter的名称(在web.xml中<filter-name>
定义的名称,或者@WebFilter
注解的filterName
属性)ServletContext getServletContext()
:获取与此Filter相关的ServletContext
对象。String getInitparameter(String name)
:根据参数名获取Filter的初始化参数值。(在配置文件中设置的参数)Enumeration<String> getInitParameterNames()
:获取所有初始化参数的名称。
创建与配置Filter
创建Filter
创建Filter主要涉及两步:
- 实现
javax.servlet.Filter
接口 - 重写其核心方法。
步骤一:创建一个Java类并实现Filter接口
我们需要创建一个普通的Java类,并让它实现 javax.servlet.Filter
接口。
1 |
|
配置Filter
在 web.xml
中配置Filter(传统方式)
这是Servlet 2.X 和早期版本的主要配置方式,在Servlet 3.0+中依然完全有效,并且在需要精准控制Filter顺序时非常有效。
打开或创建
web.xml
文件
通常位于你的Web项目的WEB-INF/web.xml路径下。声明Filter
使用<filter>
标签在web.xml中声明你的Filter。<filter-name>
:为你的Filter定义一个唯一的名称。<filter-class>
:指定你的Filter类的完全限定名。<init-param>
(可选):为Filter提供初始化参数,这些参数可以在init()
方法中通过FilterConfig
获取。
1
2
3
4
5
6
7
8
9
10
11
12<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.example.filters.MyCustomFilter</filter-class>
<init-param>
<param-name>myParam</param-name>
<param-value>HelloFilter</param-value>
</init-param>
<init-param>
<param-name>anotherParam</param-name>
<param-value>123</param-value>
</init-param>
</filter>映射Filter到URL模式:
使用
<filter-mapping>
标签将声明的Filter映射到特定的URL模式。只有匹配这些模式的请求才会经过此Filter。<filter-name>
:<url-pattern>
:/*
:拦截所有的请求/user/*
:拦截所有以/user/
开头的请求*.jsp
:拦截所有.jsp结尾的请求/specific/path
:拦截精确匹配的路径
<dispatcher>
(可选):指定Filter应该在哪种请求调度类型下被使用。常见的有:REQUEST
(默认):当客户端直接请求资源时。FORWARD
:当通过RequestDispatcher.forward()
转发时。INCLUDE
:当通过RequestDispatcher.include()
包含时。ERROR
:当请求因为错误而转发到错误页面时。ASYNC
:当请求通过异步上下文处理时(Servlet3.0+)
1
2
3
4
5
6<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>在
web.xml
中,Filter的执行顺序是根据它们在<web-app>
标签内<filter-mapping>
元素的出现顺序来决定的。容器会按照web.xml文件中<filter-mapping>
声明的从上到下的顺序来构建Filter链并依次执行。如果是url不匹配的Filter,则跳过。一直执行到最终资源。
使用注解 @WebFilter配置Filter(Servlet3.0+)
从Servlet3.0 开始,你可以使用注解来简化Filter的配置,省去了编写web.xml的麻烦。
在Filter类上添加 @WebFilter
注解
filterName
(可选):指定Filter的名称,默认为类名。urlPatterns
:一个字符串数组(可以拦截多个路径),用于指定Filter将拦截的URL模式value
:urlPatterns
的别名。如果只有一个URL模式,可以直接用value。initParams
(可选):一个@WebInitParam
数组,用于设置初始化参数。dispatcherTypes
(可选):一个DispatcherType数组,指定Filter应该在哪种请求调度类型下被调用。
1 |
|
Filter顺序:如前所述,在使用
@WebFilter
时,默认情况下Filter的执行顺序是不保证的(通常按照filterName的字母顺序或者类加载顺序),如果你需要精确的控制顺序,建议使用web.xml配置,或者在Spring等框架中使用@Order
注解
Listener
在Java Servlet中,Listener(监听器)是一种特殊的组件,它的主要作用是”监听”Web应用程序中发生的特定事件。当某个预设事件发生时,Listener就会被触发,然后执行你为它定义的特定操作。
这些事件可以是:
- Web应用程序自身的生命周期事件
- 用户会话(Session)的生命周期事件
- HTTP请求的生命周期事件
- 特定作用域(应用程序、会话、请求)中属性的添加、移除或替换事件。
Listener的核心作用:
Listener的存在,让开发者能够在这些关键时刻介入并执行自定义的逻辑,而无需修改Servlet或JSP页面的核心业务代码。它的主要作用体现在以下几个方面:
- 资源初始化与清理:
- 作用:在Web应用程序启动时,你可以使用Listener来加载配置文件、初始化数据库连接池、启动后台服务等一次性设置。而在应用程序关闭时,它可以用来关闭连接、释放资源、确保系统干净的退出。
- 职责分离:Servlet或Filter的init()方法只在特定Servlet被初始化时,这些资源才会被初始化。如果我有多个Servlet都需要这些资源,那么每个Servlet都需要重复初始化逻辑,Filter同理。Listener专注于应用程序级别的生命周期管理和全局资源的初始化/清理。它的代码更清晰,专门处理“当应用程序启动/关闭时,我需要做什么”的问题。
- 数据统计与监控:
- 作用:Listener可以用来实时跟踪网站的使用情况。例如,你可以计算当前在线用户数量,记录每天的访问量,或者监控某个特定功能的使用频率。
- 例子:你想知道有多少用户同时在线。你可以使用Listener监听会话的创建和销毁事件,每次创建就+1,销毁就-1,从而得到在线用户数量。
- 日志记录:
- 作用:在应用程序、会话或请求的关键生命周期点记录日志,帮助开发者追踪应用程序的行为、诊断问题或进行性能分析。
- 例子:每当一个用户请求进入或离开服务时,你想记录下请求的URL和处理时间。Listener就可以做到这一点。
Litener接口
Java Servlet 规范提供了一系列Listener接口,它们被设计用来监听不同范围和类型的事件。掌握这些接口是使用Listener的基础。
应用(Web容器)生命周期Listener
这类Listener用于监听整个Web应用程序的启动和关闭事件。
ServletContextListener
:
- 作用:监听Web应用程序(ServletContext)的生命周期事件。当整个Web应用程序被部署到Servlet容器(如Tomcat)并启动时,以及当应用程序被关闭或卸载时,它就会收到通知。
- 接口方法:
void contextInitialized(ServletContextEvent sce)
:当Web应用程序启动时,此方法会被调用。我们可以在这里执行全局性初始化工作。例如:- 初始化数据库连接池
- 加载应用程序的全局配置参数
- 启动后台服务或定时任务
- 初始化日志系统
void contextDestroyed(ServletContextEvent sce)
:当Web应用程序关闭或被卸载时,此方法会被调用。我们可以在这里执行资源清理工作,例如:- 关闭数据库连接池
- 释放占用的内存或文件句柄
- 停止后台服务
- 使用场景:这是最常用的Listener之一,适用于那些需要在应用程序级别进行一次性设置和清理的任务。
会话(Session)生命周期Listener
这类Listener用于监听用户会话的创建和销毁事件。
HttpSessionListener
:
- 作用:监听HTTP会话(HttpSession)的生命周期事件。当用户第一次访问网站并创建会话时,以及当会话过期或被明确销毁时,它就收到通知。
- 接口方法:
void sessionCreated(HttpSessionEvent se)
:当新的Http会话被创建时调用此方法。我们可以用这个事件:- 统计在线用户数量
- 为新会话设置默认属性
void sessionDestroyed(HttpSessionEvent se)
:当HTTP会话失效(过期或被显示调用invalidate()
)时调用此方法。我们可以用这个事件来:- 统计在线用户数
- 清理与该会话相关的资源
请求生命周期Listener
这类Listener用于监听每个HTTP请求的开始和结束事件。
ServletRequestListener
:
- 作用:监听HTTP请求(ServletRequest)的生命周期事件。在每个HTTP请求被Web容器接收并开始处理时,以及在请求处理完毕即将发送响应回客户端时,它都会收到通知。
- 接口方法:
void requestInitialized(ServletRequestEvent sre)
:当每个HTTP请求开始被处理时调用此方法。我们可以在这里:- 记录请求的开始时间,用于性能分析。
- 初始化与当前请求相关的特定资源。
void requestDestroyed(ServletRequestEvent sre)
:当每个HTTP请求处理完成并即将响应给客户端时调用此方法。我们可以在这里:- 记录请求的结束时间并计算处理耗时
- 清理与当前请求相关的临时资源
- 使用场景:常用于请求级别的性能监控,日志记录等。
属性变更Listener
这类Listener用于监听特定作用域(ServletContext
、HttpSession
、ServletRequest
)中属性的添加、移除、替换事件。当调用setAttribute()、removeAttribute()或replaceAttribute()方法时,这些Listener就会被触发。
ServletContextAttributeListener
:
- 作用:监听ServletContext(应用程序作用域)中属性的变化。
- 接口方法:
attributeAdded(ServletContextAttributeEvent scae)
attributeRemoved(ServletContextAttributeEvent scae)
attributeReplaced(ServletContextAttributeEvent scae)
HttpSessionAttributeListener
:
- 作用:监听HttpSession(会话作用域)中属性的变化
- 接口方法:
attributeAdded(HttpSessionBindingEvent hsbe)
attributeRemoved(HttpSessionBindingEvent hsbe)
attributeReplaced(HttpSessionBindingEvent hsbe)
ServletRequestAttributeListener
:
- 作用:监听ServletRequest(请求作用域)中属性的变化
- 接口方法:
attributeAdded(ServletRequestAttributeEvent srae)
attributeRemoved(ServletRequestAttributeEvent srae)
attributeReplaced(ServletRequestAttributeEvent srae)
使用场景:用于监控和响应特定作用域内数据的动态变化,例如:
- 当某个关键配置属性在应用程序级别被修改时,触发重新加载
- 跟踪会话中用户登录状态的变化
- 请求处理的过程中,根据属性变化执行特定逻辑
会话绑定Listener
这类Listener专注于监听Java对象何时被绑定到 HttpSession
或从 HttpSession
中解除绑定。
HttpSessionBindingListener
:
- 作用:这是一个特殊的Listener,它不是由容器注册的。而是被绑定到Session的Java对象本身实现。当一个实现了此接口的对象被HttpSession.setAttribute()存储到Session中时,其valueBound()方法会被调用;当它被HttpSession.removeAttribute()或Session失效时,其valueUnbound()方法会被调用。
- 接口方法:
void valueBound(HttpSessionBindingEvent event)
void valueUnbound(HttpSessionBindingEvent event)
- 使用场景:当某个对象需要知道它何时被放入或移除Session时非常有用,例如:
- 一个用户对象,当它被绑定到Session时,可以更新数据库中的在线状态。
- 一个资源句柄,当它被从Session移除时,可以自动释放资源。
Listener与原来执行代码的执行顺序
当Listener被触发时,它与原来的代码之间,通常是顺序执行的关系,而不是并行执行。
- 请求生命周期:请求开始(调用Listener)->进入Filter链->进入Servlet->离开Servlet->离开Filter->请求结束(调用Listener)
- 属性变更:
setAttribute()
等方法本身会先执行,完成属性的设置操作,然后相关的Listener才会被触发。
创建与配置Listener类
创建Listener类
步骤:
- 创建一个普通的Java类
- 让这个类实现你想要监听的事件对应的Listener接口
- 重写该接口中定义的所有方法,并在这些方法中编写你的业务逻辑
示例:
1 |
|
配置Listener
创建好Listener类后,你需要告诉Servlet容器你的这个类是一个Listener,并且希望它在特定的事件发生时被调用。
方法一:使用 web.xml
文件配置(传统方式)
这是在Servlet3.0之前以及需要最大兼容性时使用的标准方式。
- 在
<web-app>
标签的内部添加<listener>
标签 <listener>
标签内部,使用<listener-class>
标签指定Listener类的完全限定名(包括包名)
1 |
|
方法二:使用注解(Annotations)配置
从Servlet3.0开始,你可以使用 @WebListener
注解来简化Listener的配置,无需修改web.xml。这是目前主流更简洁的方式。
1 |
|
简单实战
xxy1103/javaWeb: 一个简单的Servlet、Filter、Listener的演示示例。一个简单的登录验证系统,同时统计登录在线人数。