当 web 应用在 web 容器中运行时,web 应用内部会不断地发生各种事件:如 web 应用启动、web 应用停止,用户 session 开始、用户 session 结束、用户请求到达等。
实际上,Servlet API 提供了大量监听器来监听 web 应用的内部事件,从而允许当 web 内部事件发生时回调事件监听器内的方法。
使用 Listener 只需两个步骤:
1、定义 Listener 实现类
2、通过注解或者 web.xml 文件中配置 Listener
实现 Listener 类
与 AWT 事件编程完全相似,监听不同 web 事件的监听器也不相同。常用的 web 事件监听器接口有如下几个:
ServletContextListener:用于监听 web 应用的启动和关闭。
ServletContextAttributeListener:用于监听 ServletContext 范围(application)内属性的改变。
ServletRequestListener:用于监听用户请求。
ServletRequestAttributeListener:用于监听 ServletRequest 范围(request)内属性的改变。
HttpSessionListener:用于监听用户 session 的开始和结束。
HttpSessionAttributeListener:用于监听 HttpSession 范围(session)内属性的改变。
下面先以 ServletContextListener 为例来介绍 Listener 的开发和使用,ServletContextListener 用于监听 Web 应用的启动和关闭。该 Listener 类必须实现 ServletContextListener 接口,该接口包含如下两个方法:
contextInitialized(ServletContextEvent sce):启动 web 应用时,系统调用 Listener 的该方法。
contextDestroyed(ServletContextEvent sce):关闭 web 应用时,系统调用 Listener 的该方法。
下面创建一个获取数据库连接的 Listener,该 Listener 会在应用启动时获取数据库连接,并将获取到的连接设置成 application 范围内的属性。
GetConnectionListener.java
package com.baiguiren;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.annotation.*;
@WebListener
public class GetConnectionListener implements ServletContextListener
{
// 应用启动时,该方法被调用
public void contextInitialized(ServletContextEvent sce)
{
try {
// 取得该应用的 ServletContext 实例
ServletContext application = sce.getServletContext();
// 从配置参数中获取驱动
String driver = application.getInitParameter("driver");
// 从配置参数中获取数据库 url
String url = application.getInitParameter("url");
// 从配置参数中获取数据库用户名
String user = application.getInitParameter("user");
// 从配置参数中获取密码
String pass = application.getInitParameter("pass");
// 注册驱动
Class.forName(driver);
// 获取数据库连接
Connection connection = DriverManager.getConnection(url, user, pass);
// 将数据库连接设置成 application 范围内的属性
application.setAttribute("connection", connection);
} catch (Exception ex) {
System.out.println("Listener 中获取数据库连接出现异常:" + ex.getMessage());
}
}
// 应用关闭时,该方法被调用
public void contextDestroyed(ServletContextEvent sce)
{
// 取得该应用的 ServletContext 实例
ServletContext application = sce.getServletContext();
Connection connection = (Connection)application.getAttribute("connection");
// 关闭数据库连接
if (connection != null) {
try {
connection.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
}
配置 Listener
配置 Listener 只要向 Web 应用注册 Listener 实现类即可,无需配置参数之类的东西,因此十分简单。为 web 应用配置 Listener 也有两种方式。
1、使用 @WebListener 注解修饰 Listener 实现类
2、在 web.xml 文档中使用 <listener .../> 元素进行配置
使用 @WebListener 时通常无需指定任何属性,只要使用该注解修饰 Listener 实现类即可向 Web 应用注册该监听器。
在 web.xml 中使用 <listener .../> 元素进行配置时只要配置如下子元素即可。
listener-class:指定 Listener 实现类
<listener>
<!-- 指定 Listener 的实现类 -->
<listener-class>com.baiguiren.GetConnectionListener</listener-class>
</listener>
上面的配置片段向 web 应用注册了一个 Listener,其实现类为 com.baiguiren.GetConnectionListener。当 web 应用被启动时,该 Listener 的 contextInitialized 方法被触发,该方法会获取一个 JDBC Connection,并放入 application 范围内,这样所有的 JSP 页面都可通过 application 获取数据库连接,从而非常方便地进行数据库访问。
上例中所有页面使用的都是同一个连接,较为实用的方法是:应用启动时将一个数据源(javax.sql.DataSource 实例)设置成 application 属性,而所有 JSP 页面都通过 DataSource 实例来取得数据库连接,再进行数据库访问。
使用 ServletContextAttributeListener
ServletContextAttribtuteListener 用于监听 ServletContext(application)范围内属性的变化,实现该接口的监听器需要实现如下三个方法。
attributeAdded(ServletContextAttributeEvent event):当程序把一个属性存入 application 范围时触发该方法。
attributeRemoved(ServletContextAttributeEvent event):当程序把一个属性从 application 范围删除时触发该方法。
attributeReplaced(ServletContextAttributeEvent event):当程序替换 application 范围内的属性时将触发该方法。
MyServletContextAttributeListener.java
package com.baiguiren;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.annotation.*;
@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener
{
// 当程序向 application 范围添加属性时触发
public void attributeAdded(ServletContextAttributeEvent event)
{
ServletContext application = event.getServletContext();
// 获取添加的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(application + "范围内添加了名为" + name + " , 值为" + value + "的属性!");
}
// 当程序从 application 范围删除属性时触发该方法
public void attributeRemoved(ServletContextAttributeEvent event)
{
ServletContext application = event.getServletContext();
// 获取被删除的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(application + "范围内名为" + name + " , 值为" + value + "的属性被删除了!");
}
// 当 application 范围的属性被替换时触发该方法
public void attributeReplaced(ServletContextAttributeEvent event)
{
ServletContext application = event.getServletContext();
// 获取被替换的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(application + "范围内名为" + name + " , 值为" + value + "的属性被替换了!");
}
}
使用 ServletRequestListener 和 ServletRequestAttributeListener
ServletRequestListener 用于监听用户请求的到达,实现该接口的监听器需要实现如下两个方法。
1、requestInitialized(ServletRequestEvent event):用户请求到达、被初始化时触发该方法。
2、requestDestroyed(ServletRequestEvent event):用户请求结束、被销毁时触发该方法。
ServletRequestAttributeListener 则用于监听 ServletRequest(request)范围内属性的变化,实现该接口的监听器需要实现 attributeAdded、attributeRemoved、attributeReplaced 三个方法。由此可见,ServletRequestAttributeListener 与 ServletContextAttributeListener 的作用相似,都用于监听属性的改变,只是 ServletRequestAttributeListener 监听 request 范围内属性的改变,ServletContextAttributeListener 监听的是 application 内属性的改变。
一个 Listener 可以监听多种事件,只要让该 Listener 实现多个接口就可以了:
RequestListener.java
package com.baiguiren;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.annotation.*;
@WebListener
public class RequestListener implements ServletRequestListener, ServletRequestAttributeListener
{
// 用户请求到达、被初始化时触发该方法
public void requestInitialized(ServletRequestEvent event)
{
HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
System.out.println("----发向" + request.getRequestURI() + "请求被初始化----");
}
// 当用户请求结束、被销毁时触发该方法
public void requestDestroyed(ServletRequestEvent event)
{
HttpServletRequest request = (HttpServletRequest)event.getServletRequest();
System.out.println("----发向" + request.getRequestURI() + "请求被销毁----");
}
// 当程序向 request 范围内添加属性时触发该方法
public void attributeAdded(ServletRequestAttributeEvent event)
{
ServletRequest request = event.getServletRequest();
// 获取添加的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(request + "范围内添加了名为" + name + " , 值为" + value + "的属性!");
}
// 当从 request 范围内删除属性时触发该方法
public void attributeRemoved(ServletRequestAttributeEvent event)
{
ServletRequest request = event.getServletRequest();
// 获取被删除的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(request + "范围内名为" + name + " , 值为" + value + "的属性被删除了!");
}
// 当 request 范围内的属性被替换时触发该方法
public void attributeReplaced(ServletRequestAttributeEvent event)
{
ServletRequest request = event.getServletRequest();
// 获取被替换的属性名和属性值
String name = event.getName();
Object value = event.getValue();
System.out.println(request + "范围内名为" + name + " , 值为" + value + "的属性被替换了!");
}
}