5.4 监听器
5.4.1 监听器的概念
Servlet规范中的三大接口,除了Servlet接口,Filter接口,剩下的就是==Listener接口==了。
监听器采用了设计模式中的观察者模式,监听器本身是观察者,被监听的对象是被观察者。当被监听对象的状态发生改变时,就会通知观察者,也就是监听器,监听器在收到通知后可以做出相应的处理逻辑。
Servlet监听器是Servlet规范中定义的一种特殊类,用于监听ServletContext、HttpSession和ServletRequest等域对象的创建与销毁事件,以及监听这些域对象中属性发生修改的事件。
5.4.2 监听器的分类
在Servlet2.5版本以来,定义了以下三类8种监听器:
第一大类:监听 Session、request、context 这三个==对象的创建与销毁==的:
-
HttpSessionListener
-
ServletContextListener
-
ServletRequestListener
第二大类:监听==对象属性==变化的:
-
HttpSessionAttributeListener
-
ServletContextAttributeListener
-
ServletRequestAttributeListener
第三类:监听Session 内的对象的:
-
HttpSessionBindingListener
-
HttpSessionActivationListener。
-
与上面六类不同,这两类 Listener 监听的是Session 内的对象,而非 Session 本身,不需要在 web.xml中配置。
5.5 案例演示
5.5.1 ServletContextListener
package com.servlet.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 自定义一个类,实现Servlet上下文的监听器接口
* 作用:可以监听Servlet上下文的创建和销毁时机。
*/
public class MyServletContextListener implements ServletContextListener {
/**
*
* @param sce: 当监听器被通知时,事件对象自创创建,并赋值给方法的形参
*
* 监听器的方法:都是在发生事件时,自动调用的
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
//获取时间,格式化成yyyy-MM-dd HH:mm:ss
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timeStr = sdf.format(now);
//从监听事件对象上,获取Servlet上下文对象,然后从其身上获取当前项目名称
String appName = sce.getServletContext().getContextPath();
System.out.println("项目"+appName+"的ServletContext对象在"+timeStr+"被创建");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//获取时间,格式化成yyyy-MM-dd HH:mm:ss
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timeStr = sdf.format(now);
//从监听事件对象上,获取Servlet上下文对象,然后从其身上获取当前项目名称
String appName = sce.getServletContext().getContextPath();
System.out.println("项目"+appName+"的ServletContext对象在"+timeStr+"被销毁");
}
}
在web.xml中注册监听, 放在servlet标签之前
<listener> <listener-class>com.servlet.listener.MyServletContextListener</listener-class> </listener>
5.5.2 测试
启动服务器,进行查看控制台。。。。 关闭服务器,进行查看控制台
5.5.3 HttpSessionListener
package com.servlet.listener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * 自定义一个类型,实现HttpSession监听器接口,用于监听Session对象的创建和销毁 * * 对应的方法: * sessionCreated(): Session对象创建时,被主动调用 * sessionDestroyed(): Session对象销毁时时,被主动调用 */ public class MySessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("---创建了一个session对象----"); } /** * session销毁的方式: * 1. 程序员主动调用session.invalidate() * 2. session超时 * @param se */ @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("---销毁了一个session对象----"); } }
在web.xml中注册监听, 放在servlet标签之前
<listener> <listener-class>com.servlet.listener.MySessionListener</listener-class> </listener>
5.5.4 测试
随便访问该项目的一个请求资源路径, 换一个浏览器或者重开一个浏览器,再次访问。 测试销毁: 程序员主动调用session.invalidate() 或者 session超时
5.5.5 ServletRequestListener
package com.servlet.listener; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; /** * ServletRequestListener接口,用来监听request对象的创建和销毁 * * 方法如下: * requestInitialized(): 当request对象被创建时,自动调用 * requestDestroyed(): 当request对象被销毁时,自动调用 */ public class MyRequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("------request对象被销毁了----------"); } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("------request对象被创建出来了----------"); } }
5.5.6 测试
随便访问一个该项目下的请求资源路径, 查看控制台
5.5.7 属性监听器
ServletContextAttributeListener/ServletRequestAttributeListener/HttpSessionAttributeListener三个监听接口,功能相似
package com.servlet.listener; import javax.servlet.ServletContextAttributeEvent; import javax.servlet.ServletContextAttributeListener; /** * 监听Servlet上下文对象的属性的。 * 当绑定属性,修改属性,移除属性时,会被监听到 */ public class MyContextAttributeListener implements ServletContextAttributeListener { /** * 添加属性时被触发 * @param scae */ @Override public void attributeAdded(ServletContextAttributeEvent scae) { System.out.println("添加属性时被触发"); } /** * 移除属性时被触发 * @param scae */ @Override public void attributeRemoved(ServletContextAttributeEvent scae) { System.out.println("移除属性时被触发"); } /** * 替换修改属性时被触发 * @param scae */ @Override public void attributeReplaced(ServletContextAttributeEvent scae) { System.out.println("替换修改属性时被触发"); } }
5.5.8 测试
访问三个"/sctAdd","/sctReplace","/sctRemove"请求资源路径,进行测试即可
package com.servlet.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(urlPatterns = {"/sctAdd","/sctReplace","/sctRemove"}) public class MyContextAttributeListenerTest extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext sct = getServletContext(); String uri = req.getRequestURI(); if (uri.contains("sctAdd")) { sct.setAttribute("username","zhangsan"); } else if (uri.contains("sctReplace")) { sct.setAttribute("username","lisi"); }else if (uri.contains("sctRemove")) { sct.removeAttribute("username"); } } }
5.5.9 HttpSessionBindingListener
package com.servlet.vo; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; /** * 自定义一个类,如果你想监听这个类的实例是否被绑定到session上时, * 就可以实现HttpSessionBindingListener接口 * * 或者你想监听这个类的实例从session身上移除了,也可以实现该接口 */ public class Student implements HttpSessionBindingListener { @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("--valueBound--"); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("---valueUnbound---"); } }
5.5.10 测试
测试绑定
Student student = new Student(); session.setAttribute("s1",student);
测试移除
session.removeAttribute("s1");
5.5.11 HttpSessionActivationListener
package com.servlet.vo; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionEvent; import java.io.Serializable; /** * 自定义的类型:如果该类的实例对象,随着HttpSession对象一起序列化或者反序列时,你想要监听到这种状态,那就实现 * 该接口 */ public class Teacher implements Serializable, HttpSessionActivationListener { /** * 对象被钝化时触发: 钝化,就是序列化的意思 * @param se */ @Override public void sessionWillPassivate(HttpSessionEvent se) { System.out.println("----对象被钝化了-----"); } /** * 对象被活化时触发: 活化,就是反序列化的意思 * * * 使用Session获取对象时,应该提前钝化一下,然后再读取钝化后文件里的数据 * 就会激活活化方法,如果第二次再次读取,不会触发该方法,原因是第一次读取的 * 已经保存到session里。第二次是从session中获取的,并不是从文件中获取的。 * @param se */ @Override public void sessionDidActivate(HttpSessionEvent se) { System.out.println("----对象被活化了-----"); } }
5.5.12 测试
添加配置文件:与WEB-INF并列创建一个文件夹META-INF, 创建一个context.xml配置文件,放入下面配置信息
<?xml version="1.0" encoding="UTF-8"?> <Context> <!-- classname: 配置用于持久化Session的类型。 saveOnRestart: true表示在重启时保存 maxIdleSwap:session中的对象多久不使用就钝化,单位:分钟 --> <Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true" maxIdleSwap="1"> <!-- classname: 用于持久化的文件流, directory:钝化文件的位置 --> <Store className="org.apache.catalina.session.FileStore" directory="D:/bb" /> </Manager> </Context> 默认钝化的位置: %CATALINA_BASE%\work\Catalina\localhost\项目名\下
测试钝化:绑定对象到Session上,1分钟后查看控制台输出内容
Teacher t1 = new Teacher(); session.setAttribute("t1",t1);
测试活化: 获取session对象即可,不用获取session里的t1。当然必须是同一个session对象
---Servlet(完)---