1、定义:Servlet事件监听器是Servlet规范中定义的一种特殊的类,它用于监听Web应用程序中的ServletContext、HttpSession和ServletRequest等域对象创建与销毁的事件,以及监听这些域对象中的属性发生修改的事件。
2、分类
按监听的对象可划分为:
用于监听应用程序环境对象(ServletContext)的事件监听器
用于监听用户会话对象(HttpSession)的事件监听器
用于监听请求消息对象(ServletRequest)的事件监听器
按监听的事件类型可划分为:
用于监听域对象自身的创建和销毁的事件监听器
用于监听域对象中的属性的增加和删除的事件监听器
用于监听绑定到HttpSession域中的某个对象的状态的事件监听器
3、要点
(1)、在Servlet规范中为每种事件监听器都定义了相应的接口,在编写事件监听器程序时只需实现这些接口就可以了,Web服务器根据用户编写的事件监听器所实现的接口把它注册到相应的被监听对象上。
(2)、一个web.xml文件中可以注册多个Servlet事件监听器,Web服务器按照它们在web.xml文件中的顺序来注册和加载这些监听器。
(3)、监听器的注册和调用过程都是Web容器自动完成,当发生被监听对象的被创建、修改或销毁等事件时,Web容器将调用与之相关的监听器对象的相应方法,用户在这些方法中编写的事件处理代码立即被执行。
ServletContextListener接口用于监听代表Web应用程序中的ServletContext对象的创建和销毁的事件。该接口定义了两个事件处理方法:
public void contextInitialized(ServletContextEvent sce)
当ServletContext对象被创建时,Web容器调用contextInitialized方法,该方法接收一个ServletContextEvent类型的参数,在方法内部可以通过这个参数来获得当前被创建的ServletContext对象。
publi void contextDestroyed(ServletContextEvent sce)
当ServletContext对象被销毁时,Web容器调用contextDestroyed方法。
HttpSessionListener接口用于监听Web应用程序中的用户会话对象HttpSession的创建和销毁的事件。该接口定义了两个事件处理方法:
public void sessionCreated(HttpSessionEvent se)
每当一个HttpSession对象被创建时,Web容器都会调用sessionCreated方法,该方法接收一个HttpSessionEvent类型的参数,在方法内部可以通过这个参数来获得当前被创建的HttpSession对象。
public void sessionDestroyed(HttpSessionEvent se)
每当一个HttpSession对象被销毁时,Web容器都会调用sessionDestroyed方法。
ServletRequestListener接口用于监听代表Web应用程序中的ServletRequest对象的创建和销毁的事件。该接口定义了两个事件处理方法:
public void requestInitialized(ServletRequestEvent sre)
每当一个ServletRequest对象被创建时,Web容器都会调用requestInitalized方法,该方法接收一个ServletRequestEvent类型的参数,在方法内部可以通过这个参数来获得当前被创建的ServletRequest对象。
public void requestDestroyed(SevletRequestEvent sre)
每当一个ServletRequest对象被销毁时,Web容器都会调用requestDestroyed方法。
MyListener.java
import java.util.Date; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyListener implements ServletContextListener, HttpSessionListener, ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("ServletRequest对象被销毁了"); } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("ServletRequest对象被创建了"); } @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("HttpSession对象被创建了"); } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("HttpSession对象被销毁了"); } @Override public void contextDestroyed(ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了"); } @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("ServletContext对象被创建了"); } }
XiaohuiSession.java
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class XiaohuiSession extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //等效于request.getSession(true),存在返回已有的,不存在则新建。 //request.getSession(false),不存在则不新建,返回null。 HttpSession session = request.getSession(); session.invalidate(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
web.xml(对于Servlet2.3规范,<listener>元素必须位于所有的<servlet>元素之前以及所有<filter-mapping>元素之后。Servlet2.4规范,这些同级元素之间的顺序可以任意)
<listener> <listener-class>MyListener</listener-class> </listener> <!-- session超时时间为1分钟 --> <session-config> <session-timeout>1</session-timeout> </session-config> <servlet> <servlet-name>XiaohuiSession</servlet-name> <servlet-class>XiaohuiSession</servlet-class> </servlet> <servlet-mapping> <servlet-name>XiaohuiSession</servlet-name> <url-pattern>/servlet/XiaohuiSession</url-pattern> </servlet-mapping>
借此说说session与浏览器的对应关系:
(1)、session是与浏览器一一对应的,而不是与一个访问页面。
(2)、具体地说,在一个浏览器访问某个页面创建(how to createhttp://www.360doc.com/content/12/0511/12/1542811_210284774.shtml)一个session后,只要该浏览器没有关闭,不管新建窗口重新访问这个页面,还是双击击桌面图标再打开同样的浏览器访问这个页面,在这个session的有效期(how to set 有效期?http://fengpy2009.iteye.com/blog/834717)内,不会创建新的session。浏览器还是属于这个会话。
(3)、何为关闭浏览器?要明白:即使这个访问页面所在浏览器关闭了,但关闭这个页面所在浏览器之前重新点击桌面图标打开了同样的浏览器,这都不算关闭了浏览器。真正的关闭是:这个浏览器一个窗口的都不存在,不管在访问这个页面前打开的浏览器还是访问之后再打开的浏览器,总之在任务栏(哪怕隐藏了)不存在该浏览器这个任务才算关闭。
(4)、如果真正关闭了浏览器(是真正关闭),重新打开浏览器访问这个页面时,哪怕旧的session没有销毁,这次访问依然会创建一个新的session,浏览器属于新的这个会话。同时也要明白:关闭浏览器不等于session被销毁,不关闭浏览器不等于session一直都存在,当session超时后就被销毁(如何不超时就销毁?session.invalidate(),见下面测试二)。
test.jsp
<body> 这是一个测试监听器的页面! <a href="servlet/XiaohuiSession">马上销毁session?</a> </body>
测试一:
部署项目后,启动Tomcat:
直接关闭Tomcat后台,虽然ServletContext对象被销毁了,但无法看到。故意让项目Reloading(如修改某个类):
在浏览器访问test.jsp:
连续刷新几次:
1分钟以后(通过打印系统时间System.out.println(new Date())发现真的是1分钟的)
测试二:
启动Tomcat后,访问test.jsp,然后点击链接(在session超时前点击),发现session马上被销毁:
ServletContextAttributeListener:用于监听ServletContext对象中的属性变更信息
HttpSessionAttributeListener:用于监听HttpSession对象中的属性变更信息
ServletRequestAttributeListener:用于监听ServletRequest对象中的属性变更信息
这三个接口都定义了三个方法来处理被监听对象中的属性的增加、删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接收参数的类型不同。
attributeAdded方法
public void attributeAdded(ServletContextAttributeEvent scad) public void attributeAdded(HttpSessionBindingEvent se) public void attributeAdded(ServletRequestAttributeEvent srae)
attributeRemoved方法
public void attributeRemoved(ServletContextAttributeEvent scad) public void attributeRemoved(HttpSessionBindingEvent se) public void attributeRemoved(ServletRequestAttributeEvent srae)
attitudeReplaced方法
public void attitudeReplaced(ServletContextAttributeEvent scad) public void attitudeReplaced(HttpSessionBindingEvent se) public void attitudeReplaced(ServletRequestAttributeEvent srae)
MyAttributeListener.java
import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; public class MyAttributeListener implements ServletContextAttributeListener, HttpSessionAttributeListener, ServletRequestAttributeListener { @Override public void attributeAdded(ServletContextAttributeEvent scae) { System.out.println("ServletContext对象中增加了一个名为" + scae.getName() + "的属性,该属性值为" + scae.getValue()); } @Override public void attributeRemoved(ServletContextAttributeEvent scae) { System.out.println("ServletContext对象中的" + scae.getName() + "的属性被删除了"); } @Override public void attributeReplaced(ServletContextAttributeEvent scae) { System.out.println("ServletContext对象中的" + scae.getName() + "属性的值由" + scae.getValue() + "替换成了" + scae.getServletContext().getAttribute(scae.getName())); } @Override public void attributeAdded(HttpSessionBindingEvent hbe) { System.out.println("HttpSession对象中增加了一个名为" + hbe.getName() + "的属性,该属性值为" + hbe.getValue()); } @Override public void attributeRemoved(HttpSessionBindingEvent hbe) { System.out.println("HttpSession对象中的" + hbe.getName() + "属性被删除了"); } @Override public void attributeReplaced(HttpSessionBindingEvent hbe) { System.out.println("HttpSession对象中的" + hbe.getName() + "属性的值由" + hbe.getValue() + "替换成了" + hbe.getSession().getAttribute(hbe.getName())); } @Override public void attributeAdded(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest对象中增加了一个名为" + srae.getName() + "的属性,该属性值为" + srae.getValue()); } @Override public void attributeRemoved(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest对象中的" + srae.getName() + "属性被删除了"); } @Override public void attributeReplaced(ServletRequestAttributeEvent srae) { System.out.println("ServletRequest对象中的" + srae.getName() + "属性的值由" + srae.getValue() + "替换成了" + srae.getServletRequest().getAttribute(srae.getName())); } }
web.xml
<listener> <listener-class>MyAttributeListener</listener-class> </listener>
test.jsp
<body> <h4>这是一个测试对象属性信息监听器的页面!</h4> <% getServletContext().setAttribute("username", "zhangsan"); getServletContext().setAttribute("username", "lisi"); getServletContext().removeAttribute("username"); session.setAttribute("username", "zhangsan"); session.setAttribute("username", "lisi"); session.removeAttribute("username"); request.setAttribute("username", "zhangsan"); request.setAttribute("usernmae", "lisi"); request.removeAttribute("username"); %> </body>
启动Tomcat,访问test.jsp页面:
保存到Session域中的对象可以有多种状态:绑定(保存)到Session域中、从Session域中解除绑定、随Session对象持久化(钝化)到一个存储设备中,随Session对象从一个存储设备中恢复(活化)。在Servlet规范中还定义了两个特殊的监听接口来帮助JavaBean对象了解自己在Session域中的这些状态,这两个接口分别是HttpSessionBindingListener和HttpSessionActivationListener,实现这两个接口的类不需要在web.xml文件中注册(即不需要写<listener>标签)。