在JavaWeb开发中,常见的监听器(Listener)用于监听Web应用程序、会话(Session)、请求(Request)的生命周期或属性的变化。
应用域监听器:
应用域监听器(也称为ServletContextListener
)用于监听Web应用程序的启动和关闭等生命周期事件。
1. ServletContextListener概述
ServletContextListener
监听ServletContext
对象的生命周期,ServletContext
代表Web应用程序的全局域对象,通常是整个应用启动和关闭时触发监听事件。可以通过监听这些事件来在应用程序启动时执行初始化任务,或者在应用程序销毁时进行资源清理。
2. ServletContextListener接口中的方法
ServletContextListener
接口主要有两个方法,分别对应Web应用的启动和关闭:
-
contextInitialized(ServletContextEvent sce)
: 在Web应用启动时执行。当服务器启动并加载Web应用时,该方法会被调用一次。通常在这里编写初始化应用的代码。常见用途:
- 加载应用配置文件。
- 初始化资源(如数据库连接池、缓存、线程池等)。
- 创建和设置全局共享的数据或对象
@Override public void contextInitialized(ServletContextEvent sce) { ServletContext application = sce.getServletContext(); System.out.println(application.hashCode() + "应用域初始化了"); }
-
contextDestroyed(ServletContextEvent sce)
: 在Web应用关闭时执行。当服务器关闭Web应用或重新部署Web应用时,该方法会被调用一次。通常在这里编写清理资源的代码。常见用途:
- 关闭数据库连接。
- 释放应用中使用的系统资源。
- 停止后台线程或任务。
@Override public void contextDestroyed(ServletContextEvent sce) { ServletContext application = sce.getServletContext(); System.out.println(application.hashCode() + "应用域销毁了"); }
ServletContextEvent:
简称sce
是Java Web开发中与ServletContextListener
监听器相关的一个事件类。它封装了ServletContext
对象,用于在监听ServletContext
(应用域对象)的生命周期变化时,传递相关信息。
1. ServletContextEvent
的定义
ServletContextEvent
是java.servlet
包中的一个类,继承自java.util.EventObject
,用于表示ServletContext
对象发生的事件。例如,应用程序启动或关闭时会生成一ServletContextEvent
事件对象,并传递给ServletContextListener
监听器。
2. ServletContextEvent
的作用
当Web应用程序启动或关闭时,ServletContextListener
的contextInitialized()
和contextDestroyed()
方法会被调用,并且这些方法会接收到一个ServletContextEvent
对象作为参数。通过这个对象,监听器可以获取到当前的ServletContext
,从而操作应用范围的资源或数据。
3. ServletContextListener的典型用法
示例:
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.annotation.WebListener;
@WebListener
public class MyAppListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// Web应用启动时执行
System.out.println("应用已启动,执行初始化操作...");
// 执行初始化操作,如加载配置文件、初始化数据库连接等
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Web应用关闭时执行
System.out.println("应用已关闭,执行清理操作...");
// 执行清理操作,如关闭数据库连接、释放资源等
}
}
3.2. 配置监听器
监听器可以通过两种方式进行配置:
-
方式1:在
web.xml
中配置监听器 在web.xml
文件中配置监听器类:
<listener>
<listener-class>com.example.MyAppListener</listener-class>
</listener>
方式2:使用注解@WebListener
在Java类上直接使用@WebListener
注解,如上面示例中的MyAppListener
。
4. ServletContextListener的应用场景
4.1. 初始化全局资源
在应用启动时,开发者可以利用contextInitialized()
方法初始化应用所需的全局资源。这些资源在整个应用生命周期中可能会被不同的组件或Servlet使用。例如:
- 数据库连接池的初始化:在应用启动时创建数据库连接池,确保后续的请求可以直接使用。
- 加载配置文件:可以加载应用程序的配置文件(如
application.properties
或config.xml
),并将其存储到ServletContext
中,以便应用程序中的其他组件读取。@Override public void contextInitialized(ServletContextEvent sce) { // 初始化数据库连接池 DataSource dataSource = setupDataSource(); sce.getServletContext().setAttribute("dataSource", dataSource); // 加载配置文件 Properties appConfig = new Properties(); try (InputStream input = sce.getServletContext().getResourceAsStream("/WEB-INF/appConfig.properties")) { appConfig.load(input); } catch (IOException e) { e.printStackTrace(); } sce.getServletContext().setAttribute("appConfig", appConfig); }
4.2. 释放资源
在应用关闭时,contextDestroyed()
方法可以被用来释放全局资源或执行清理操作,例如:
- 关闭数据库连接池:确保所有数据库连接都正确关闭,防止资源泄露。
- 停止后台线程:如果应用在后台启动了定时任务或线程池,在关闭应用时应该停止这些任务,以释放系统资源。
@Override public void contextDestroyed(ServletContextEvent sce) { // 获取数据库连接池 DataSource dataSource = (DataSource) sce.getServletContext().getAttribute("dataSource"); if (dataSource != null) { // 关闭连接池 closeDataSource(dataSource); } // 停止后台任务 ScheduledExecutorService scheduler = (ScheduledExecutorService) sce.getServletContext().getAttribute("scheduler"); if (scheduler != null) { scheduler.shutdown(); } }
ServletContextAttributeListener 监听ServletContext中属性的添加、移除和修改
方法名 | 描述 |
---|---|
attributeAdded | 当在ServletContext 中添加属性时调用 |
attributeRemoved | 当从ServletContext 中移除属性时调用 |
attributeReplaced | 当在ServletContext 中的属性被替换或修改时调用 |
此时要注意添加另一个接口ServletContextAttributeListener
package com.listener;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebListener;
@WebListener
public class MyApplicationListener implements ServletContextListener , ServletContextAttributeListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext application = sce.getServletContext();
System.out.println(application.hashCode() + "应用域初始化了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext application = sce.getServletContext();
System.out.println(application.hashCode() + "应用域销毁了");
}
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
ServletContext application = scae.getServletContext();
// 增加的数据
String key = scae.getName();
Object value = scae.getValue();
System.out.println(application.hashCode()+"应用域 增加了"+key +": "+value);
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
ServletContext application = scae.getServletContext();
// 增加的数据
String key = scae.getName();
Object value = scae.getValue();
System.out.println(application.hashCode()+"应用域 删除了"+key +": "+value);
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
ServletContext application = scae.getServletContext();
// 增加的数据
String key = scae.getName();
Object value = scae.getValue();// 获取的是旧的值
Object value2 = scae.getValue();// 获取的是新的值
System.out.println(application.hashCode()+"应用域 修改了"+key +"的value值: "+value+" 变成了" + value2);
}
}
servlet1:
package com.servlet;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import java.io.IOException;
@WebServlet("/servlet1")
public class servlet1 extends HttpServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 向应用域中放入数据
ServletContext servletContext = servletRequest.getServletContext();
servletContext.setAttribute("keya","valuea");
}
}
servlet2:
package com.servlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servlet2")
public class servlet2 extends HttpServlet {
// 向应用域改数据
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
servletContext.setAttribute("keya","valueXX");
}
}
servlet3:
package com.servlet;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servlet3")
public class servlet3 extends HttpServlet {
// 删除应用域的数据
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = req.getServletContext();
servletContext.removeAttribute("keya");
}
}
在网页依次访问servlet1,servlet2,servlet3,得到控制台的显示:
session域和请求域的监听器其实都差不多,这里就以表格的形式来说明
HttpSessionBindingListener
方法一览
方法名 | 作用 |
---|---|
valueBound(HttpSessionBindingEvent event) | 该类的实例放到 Session 域中时调用 |
valueUnbound(HttpSessionBindingEvent event) | 该类的实例从 Session 中移除时调用 |