一、什么是监听器
监听器是Servlet
规范中定义的一种特殊类,用于监听ServletContext
、HttpSession
和ServletRequest
等域对象的创建和销毁事件,它还可以监听域对象的属性发生修改的事件,可以在事件发生前或者发生后做一些必要的处理。
在Servlet中要创建监听器类首先需要新建一个类并继承相应的监听器接口,实现接口中定义的方法,然后在web.xml
文件中注册相应的监听器即可。如果一个web.xml文件中注册了多个监听器,则监听器的启动顺序按照在web.xml中的注册顺序启动。如果一个web.xml文件中同时定义了监听器、过滤器和Servlet,那么web容器会先加载监听器、再加载过滤器最后加载Servlet。
二、监听器的分类
按照监听的事件可以将监听器划分为以下三类:
1、监听域对象自身的创建和销毁的事件监听器;
2、监听域对象中属性的增加和删除的事件监听器;
3、监听绑定到HttpSession域中某个对象状态的事件监听器。
三、常用监听器接口
1、监听域对象自身的创建和销毁的事件监听器
这一类监听器主要监听ServletContext
、HttpSession
和ServletRequest
这三个域对象创建和销毁的事件,要实现这一类监听器,需要继承ServletContextListener
、HttpSessionListener
或者ServletRequestListener
接口,分别来对这三个域对象进行分析。
(1)首先是继承ServletContextListener
,这一类监听器主要监听应用程序环境发生的事件,而要继承ServletContextListener,就需要实现该接口中的contextInitialized
和contextDestroyed
方法,简单地说就是启动服务器创建应用程序上下文时和关闭服务器销毁程序上下文时执行的操作。可以参考下面的实例:
package com.imooc.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* Application Lifecycle Listener implementation class MyServletContextListener
*
*/
public class MyServletContextListener implements ServletContextListener {
/**
* Default constructor.
*/
public MyServletContextListener() {
}
/**
* @see ServletContextListener#contextDestroyed(ServletContextEvent)
*/
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("contextDestroyed");
}
/**
* @see ServletContextListener#contextInitialized(ServletContextEvent)
*/
public void contextInitialized(ServletContextEvent arg0) {
String name = arg0.getServletContext().getInitParameter("name");
System.out.println("contextInitialized : name=" + name);
}
}
这个例子中在服务器启动时,运行contextInitialized
方法,可以通过ServletContextEvent
来获取服务器启动的初始参数,这些初始参数都是在web.xml文件中配置的。当服务器停止时,会执行监听器的contextDestroyed
方法,进行资源的回收等操作。定义了监听器之后,就可以在web.xml中注册监听器了,注册内容如下,同时还定义了初始化参数。
<listener>
<listener-class>com.imooc.listener.MyServletContextListener</listener-class>
</listener>
<context-param>
<param-name>name</param-name>
<param-value>imooc</param-value>
</context-param>
这时启动服务器,观察启动日志,会发现打印出如下的日志,获取到了初始化参数name的值并打印出来,说明监听器已经启动了。
……
八月 23, 2016 8:38:19 下午 org.apache.jasper.servlet.TldScanner scanJars
信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
contextInitialized : name=imooc
八月 23, 2016 8:38:19 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-8080"]
……
再停止服务器,会发现打印了执行监听器里destroy方法的内容。
……
八月 23, 2016 8:40:58 下午 org.apache.catalina.core.StandardService stopInternal
信息: Stopping service Catalina
contextDestroyed
八月 23, 2016 8:40:58 下午 org.apache.coyote.AbstractProtocol stop
信息: Stopping ProtocolHandler ["http-nio-8080"]
……
(2)接下来看继承HttpSessionListener
的监听器,这一类监听器监听的是用户会话对象的创建和销毁事件,定义如下的监听器,在创建和销毁会话对象时将输出一句话。
package com.imooc.listener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* Application Lifecycle Listener implementation class MySessionListener
*
*/
public class MySessionListener implements HttpSessionListener {
/**
* Default constructor.
*/
public MySessionListener() {
}
/**
* @see HttpSessionListener#sessionCreated(HttpSessionEvent)
*/
public void sessionCreated(HttpSessionEvent arg0) {
System.out.println("sessionCreated");
}
/**
* @see HttpSessionListener#sessionDestroyed(HttpSessionEvent)
*/
public void sessionDestroyed(HttpSessionEvent arg0) {
System.out.println("sessionDestroyed");
}
}
接着需要在web.xml中注册相应的监听器。
<listener>
<listener-class>com.imooc.listener.MySessionListener</listener-class>
</listener>
启动项目,打开浏览器访问初始页面index.jsp,会发现控制台打印sessionCreated,关闭用户会话,这时会打印sessionDestroyed。
(3)最后看一下继承ServletRequestListener接口的监听器,这一类监听器监听的是用户请求对象,当请求被创建和销毁时会执行监听器中的方法。例如定义如下的监听器来监听请求对象的创建和销毁动作,当请求被创建时会通过ServletRequestEvent对象获取请求参数name的值。
package com.imooc.listener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
/**
* Application Lifecycle Listener implementation class MyRequestListener
*
*/
public class MyRequestListener implements ServletRequestListener {
/**
* Default constructor.
*/
public MyRequestListener() {
}
/**
* @see ServletRequestListener#requestDestroyed(ServletRequestEvent)
*/
public void requestDestroyed(ServletRequestEvent arg0) {
System.out.println("requestDestroyed");
}
/**
* @see ServletRequestListener#requestInitialized(ServletRequestEvent)
*/
public void requestInitialized(ServletRequestEvent arg0) {
System.out.println("requestDestroyed : name=" + arg0.getServletRequest().getParameter("name"));
}
}
启动项目,访问index.jsp?name=imooc,这里定义一个名为name的参数,参数值是imooc,提交请求,这时后台会打印
requestDestroyed : name=imooc
requestDestroyed
首先用户提交请求时,请求对象被创建,监听器监听到请求对象创建的事件,这时执行监听器的initialize方法,同时监听器获取到参数名为name的参数值并打印。因为request对象只在一次请求有效,所以服务器返回响应后请求对象便被销毁,这时执行监听器的destory方法。
2、监听域对象中属性的增加和删除的事件监听器
这一类监听器主要监听ServletContext、HttpSession和ServletRequest这三个域对象中属性的创建、销毁和修改的事件,要实现这三种监听器,就需要继承ServletContextAttributeListener
、HttpSessionAttributeListener
和ServletRequestAttributeListener
这三个接口,并实现接口中的方法,下面通过一个实例来分析这三种类型的监听器。首先分别定义三个监听器分别表示这三种类型的监听器,定义如下,这三个监听器都是监听域对象中属性的创建、修改和删除动作并打印出相应的属性名称。
package com.imooc.listener;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
/**
* Application Lifecycle Listener implementation class MyServletContextAttributeListener
*
*/
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
/**
* Default constructor.
*/
public MyServletContextAttributeListener() {
}
/**
* @see ServletContextAttributeListener#attributeAdded(ServletContextAttributeEvent)
*/
public void attributeAdded(ServletContextAttributeEvent arg0) {
System.out.println("ServletContext_attributeAdded : name=" + arg0.getName());
}
/**
* @see ServletContextAttributeListener#attributeRemoved(ServletContextAttributeEvent)
*/
public void attributeRemoved(ServletContextAttributeEvent arg0) {
System.out.println("ServletContext_attributeRemoved : name=" + arg0.getName());
}
/**
* @see ServletContextAttributeListener#attributeReplaced(ServletContextAttributeEvent)
*/
public void attributeReplaced(ServletContextAttributeEvent arg0) {
System.out.println("ServletContext_attributeReplaced : name=" + arg0.getName());
}
}
package com.imooc.listener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
/**
* Application Lifecycle Listener implementation class MySessionAttributeListener
*
*/
public class MySessionAttributeListener implements HttpSessionAttributeListener {
/**
* Default constructor.
*/
public MySessionAttributeListener() {
}
/**
* @see HttpSessionAttributeListener#attributeAdded(HttpSessionBindingEvent)
*/
public void attributeAdded(HttpSessionBindingEvent arg0) {
System.out.println("Session_attributeAdded : name=" + arg0.getName());
}
/**
* @see HttpSessionAttributeListener#attributeRemoved(HttpSessionBindingEvent)
*/
public void attributeRemoved(HttpSessionBindingEvent arg0) {
System.out.println("Session_attributeRemoved : name=" + arg0.getName());
}
/**
* @see HttpSessionAttributeListener#attributeReplaced(HttpSessionBindingEvent)
*/
public void attributeReplaced(HttpSessionBindingEvent arg0) {
System.out.println("Session_attributeReplaced : name=" + arg0.getName());
}
}
package com.imooc.listener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
/**
* Application Lifecycle Listener implementation class MyRequestAttributeListener
*
*/
public class MyRequestAttributeListener implements ServletRequestAttributeListener {
/**
* Default constructor.
*/
public MyRequestAttributeListener() {
}
/**
* @see ServletRequestAttributeListener#attributeRemoved(ServletRequestAttributeEvent)
*/
public void attributeRemoved(ServletRequestAttributeEvent arg0) {
System.out.println("Request_attributeRemoved : name=" + arg0.getName());
}
/**
* @see ServletRequestAttributeListener#attributeAdded(ServletRequestAttributeEvent)
*/
public void attributeAdded(ServletRequestAttributeEvent arg0) {
System.out.println("Request_attributeAdded : name=" + arg0.getName());
}
/**
* @see ServletRequestAttributeListener#attributeReplaced(ServletRequestAttributeEvent)
*/
public void attributeReplaced(ServletRequestAttributeEvent arg0) {
System.out.println("Request_attributeReplaced : name=" + arg0.getName());
}
}
定义好三个监听器之后就可以在web.xml中进行注册了。
<listener>
<listener-class>com.imooc.listener.MyServletContextAttributeListener</listener-class>
</listener>
<listener>
<listener-class>com.imooc.listener.MySessionAttributeListener</listener-class>
</listener>
<listener>
<listener-class>com.imooc.listener.MyRequestAttributeListener</listener-class>
</listener>
这时可以写几个页面来做一下测试,首先写一个index.jsp,页面里只包括两个超链接,链接到连个页面分别做添加属性和删除属性操作。
<a href="addAttribute.jsp">Add attribute</a><br/>
<a href="removeAttribute.jsp">Remove attribute</a>
addAttribute.jsp
页面如下,这个页面里可以给三个域对象分别新增一个属性。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
</head>
<body>
Add attribute
<a href="addAttribute.jsp">Add attribute</a>
<a href="removeAttribute.jsp">Remove attribute</a>
<%
request.setAttribute("attributeName", "attributeValue");
request.getSession().setAttribute("attributeName", "attributeValue");
request.getServletContext().setAttribute("attributeName", "attributeValue");
%>
</body>
</html>
removeAttribute.jsp
页面如下,页面将三个域对象中的属性移除。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
</head>
<body>
Remove attribute
<a href="addAttribute.jsp">Add attribute</a>
<a href="removeAttribute.jsp">Remove attribute</a>
<%
request.removeAttribute("attributeName");
request.getSession().removeAttribute("attributeName");
request.getServletContext().removeAttribute("attributeName");
%>
</body>
</html>
这时再点击removeAttribute.jsp
,后台打印如下,可以发现request域对象的属性没有被移除,这里的原理和上面域对象属性的修改是一样的。
Session_attributeRemoved : name=attributeName
ServletContext_attributeRemoved : name=attributeName
要想能够执行Request域对象监听器里属性的修改和删除方法,可以在addAttribute.jsp里稍作修改,在程序最后重新给属性赋值,然后删除属性,在运行程序,就可以了,具体代码如下。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
</head>
<body>
Add attribute
<a href="addAttribute.jsp">Add attribute</a>
<a href="removeAttribute.jsp">Remove attribute</a>
<%
request.setAttribute("attributeName", "attributeValue");
request.getSession().setAttribute("attributeName", "attributeValue");
request.getServletContext().setAttribute("attributeName", "attributeValue");
request.setAttribute("attributeName", "attributeValue");
request.removeAttribute("attributeName");
%>
</body>
</html>
运行程序,访问addAttribute.jsp,这时控制台打印如下信息,说明监听器的修改和删除属性的方法也被执行到了。
Request_attributeAdded : name=attributeName
Session_attributeAdded : name=attributeName
ServletContext_attributeAdded : name=attributeName
Request_attributeReplaced : name=attributeName
Request_attributeRemoved : name=attributeName
3、监听绑定到HttpSession域中某个对象状态的事件监听器
下面来看最后一类监听器,这类监听器主要监听的是绑定到HttpSession
域中某个对象状态的事件,HttpSession
中对象的状态有两种:绑定与解除绑定、钝化与活化。所谓的绑定与解除绑定,就是指在HttpSession中将某个对象设置为属性值或者移除某个属性的值。而钝化是指服务器会将不常使用的Session对象暂时序列化到系统文件或数据库中,而活化就是将暂存在系统文件或数据库中的Session对象反序列化到服务器中,当Tomcat服务器被关闭或者重启时,Tomcat会将Session对象钝化到服务器文件系统中,当服务器被重新加载时,Session对象也会被钝化。要实现这两种的监听器,就需要继承HttpSessionBindingListener
和HttpSessionActivationListener
,下面分别来分析。
(1)先来看继承HttpSessionBindingListener
接口的监听器,要注意这一类监听器在被定义之后不需要在web.xml文件中进行注册,这一类的监听器可以定义成一个JavaBean的形式,可以在里面设置属性等,同时要注意的是这一类的监听器也要实现Serializable接口,例如定义一个User类如下,实现了valueBound方法用于监听绑定动作、valueUnbound方法用于监听解除绑定动作。
package com.imooc.listener;
import java.io.Serializable;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;
/**
* Application Lifecycle Listener implementation class User
*
*/
public class User implements HttpSessionBindingListener, Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public User() {
}
/**
* @see HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
*/
public void valueBound(HttpSessionBindingEvent paramHttpSessionBindingEvent) {
System.out.println("valueBound : name=" + paramHttpSessionBindingEvent.getName());
}
/**
* @see HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
*/
public void valueUnbound(HttpSessionBindingEvent paramHttpSessionBindingEvent) {
System.out.println("valueUnbound : name=" + paramHttpSessionBindingEvent.getName());
}
}
现在就可以使用上面的addAttribute.jsp和removeAttribute.jsp来验证一下了,修改两个页面的代码。
<%@page import="com.imooc.listener.User"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
</head>
<body>
Add attribute
<a href="addAttribute.jsp">Add attribute</a>
<a href="removeAttribute.jsp">Remove attribute</a>
<%
request.setAttribute("attributeName", "attributeValue");
request.getSession().setAttribute("attributeName", "attributeValue");
request.getServletContext().setAttribute("attributeName", "attributeValue");
request.setAttribute("attributeName", "attributeValue");
request.removeAttribute("attributeName");
request.getSession().setAttribute("currentUser", new User());
%>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
</head>
<body>
Remove attribute
<a href="addAttribute.jsp">Add attribute</a>
<a href="removeAttribute.jsp">Remove attribute</a>
<%
request.removeAttribute("attributeName");
request.getSession().removeAttribute("attributeName");
request.getServletContext().removeAttribute("attributeName");
request.getSession().removeAttribute("currentUser");
%>
</body>
</html>
我们会发现这里绑定对象与解除绑定使用的仍然是Session的setAttribute
和removeAttribute
方法,这和前面设置属性是一样的,那么这种绑定对象的监听器与设置属性的监听器有什么区别呢?绑定对象的监听器只监听某种类型的对象的绑定与解绑操作,而设置属性的监听器监听的是所有设置属性的动作。运行程序,点击addAttribute.jsp,这时后台打印valueBound : name=currentUser,再点击removeAttribute,这时后台打印valueUnbound : name=currentUser,说明监听器监听成功。
(2)最后来看一下继承HttpSessionActivationListener的监听器,这一类监听器不常用,了解一下即可。