监听器
1 事件三要素
a)事件源:操作事件的对象,例如:窗体Frame
b)事件监听器:事件监听器监听事件源,例如WindowListner,它是一个接口
c)事件,例如:单击事件,通过事件,可以取得事件源
2 适配器模式
a)当一个接口有较多的方法时,而实现类只需对其中少数几个实现,此时可以使用适配器模式
b)适配器模式常用于GUI编程,而web监听器中并不存在Adapter(适配器)
*3 八种web监听器详解
a)Web中有三个事件源,分别是ServletContext->HttpSession->ServletRequest
***注意:Listener都是单例模式,当web应用启动时创建Listener实例,并在web应用停止时销毁Listener实例。
b)ServletContext对象
>>创建和销毁:ServletContextListener.
Web容器部署时容器自动调用contextInitialized()方法,
Web容器重新部署时容器自动调用contextDestroyed()方法。
>>属性变化:ServletContextAttributeListner
application.setAttirbute("name","jack") ----> void attributeAdded(ServletContextAttributeEvent event)
setAttribute("name","杰克") ----> void attributeReplaced(ServletContextAttributeEvent event)
removeAttribute("name") ----> void attributeRemoved(ServletContextAttributeEvent event)
案例:
1)利用监听器初始化数据库
code: public class MyServletContextListener implements ServletContextListener { private ContextDao dao = new ContextDao(); @Override public void contextInitialized(ServletContextEvent event) { try { dao.createTable(); System.out.println("创建表成功!"); dao.insertTable("jack"); System.out.println("插入数据成功!"); } catch (SQLException e) { e.printStackTrace(); } } @Override public void contextDestroyed(ServletContextEvent event) { try { dao.dropTable(); System.out.println("删除表成功!"); } catch (SQLException e) { e.printStackTrace(); } } } web.xml <listener> <listener-class>listener.context.MyServletContextListener</listener-class> </listener>
2) 指定时间往数据库插入一次数据,5秒的延迟
public class MyServletContextListener1 implements ServletContextListener { private TimerDao dao = new TimerDao(); private Timer timer; private TimerTask task; public MyServletContextListener1() { this.timer = new Timer(); /*初始化TimerTask对象*/ task = new TimerTask() { @Override public void run() { try { dao.insertTable(UUID.randomUUID().toString(), new Date()); } catch (SQLException e) { e.printStackTrace(); } } }; } public void contextInitialized(ServletContextEvent event) { try { dao.createTable(); //每隔5秒像数据库插入一条数据 timer.schedule(task, 0, 1000*5); } catch (SQLException e) { e.printStackTrace(); } } public void contextDestroyed(ServletContextEvent event) { try { dao.destoryTable(); } catch (SQLException e) { e.printStackTrace(); } } }
3)ServletContextAttributeListener测试
code:
//listener public class MyServletContextAttributeListener implements ServletContextAttributeListener { @Override public void attributeAdded(ServletContextAttributeEvent event) { System.out.println("attributeAdded==>" + event.getName() + " : " + event.getValue()); } @Override public void attributeReplaced(ServletContextAttributeEvent event) { //注意:此时的getValue得到的是替换前的value值。 System.out.println("attributeReplaced==>" + event.getName() + " : " + event.getValue()); } @Override public void attributeRemoved(ServletContextAttributeEvent event) { System.out.println("attriattributeRemoved==>" + event.getName() + " : " + event.getValue()); } } //java application.setAttribute("name", "jack"); application.setAttribute("name", "杰克"); application.removeAttribute("name"); //输出结果: attributeAdded==>name : jack attributeReplaced==>name : jack attriattributeRemoved==>name : 杰克 c)ServletRequest对象 >>创建和销毁:ServletRequestListener. 每次请求容器自动调用requestInitialized()方法, 响应完毕容器自动调用requestDestroyed()方法 >>属性变化:ServletRequestAttributeListner(与ServletContextAttributeListener类似,例子省略) request.setAttirbute("name","jack") setAttribute("name","杰克") removeAttribute("name")
案例:
1)回写显示访问此网站的次数,访问者ip,以及访问者本地时间【js】。
code: public class MyServletRequestListener implements ServletRequestListener { private Integer queryNum = 0; @Override public void requestDestroyed(ServletRequestEvent event) { System.out.println("requestDestroyed()"); } @Override public void requestInitialized(ServletRequestEvent event) { HttpServletRequest request = (HttpServletRequest) event.getServletRequest(); String ip = request.getRemoteAddr(); synchronized (this) { queryNum++; } request.setAttribute("ip", ip); request.setAttribute("num", queryNum); System.out.println("requestInitialized()"); } } js: <script type="text/javascript"> function nowTime() { window.setInterval("updateTime()", 1000); } function updateTime() { var nowDate = new Date().toLocaleString(); //定位区域并输出 var spanElement = document.getElementById("spanId"); spanElement.innerHTML = nowDate; } </script> </head> <body onload="nowTime()"> 您的ip:${requestScope.ip} <br/> 网站访问次数:${requestScope.num} <br/> 当前时间:<span id="spanId"></span> <br/> </body>
d)HttpSession对象
>>创建和销毁:HttpSessionListener.
当Web容器创建HttpSession对象时容器自动调用sessionCreated()方法
当Web容器销毁HttpSession对象时容器自动调用sessionDestroyed()方法
>>属性变化:HttpSessionAttributeListner (与ServletContextAttributeListener类似,例子省略)
session.setAttirbute("name","jack")
setAttribute("name","杰克")
removeAttribute("name")
案例:
1)HttpSessionListener调用函数测试
web.xml <listener> <listener-class>listener.session.MyHttpSessionListener</listener-class> </listener> <!-- 设置session的过期时间为1分钟,默认为30分钟 【注意:实际上时间并不为1分钟】--> <session-config> <session-timeout>1</session-timeout> </session-config> code: public class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent event) { HttpSession session = event.getSession(); //若为新创建的session if (session.isNew()) { System.out.println("sessionCreated, ID:" + session.getId()); } } @Override public void sessionDestroyed(HttpSessionEvent event) { HttpSession session = event.getSession(); if (!session.isNew()) { System.out.println("sessionDestroyed, ID:" + session.getId()); } } }
2)利用Session扫描器:精确控制session的声明周期
code:
public class SessionScanner implements HttpSessionListener, ServletContextListener { private List<HttpSession> sessionList = Collections.synchronizedList(new ArrayList<HttpSession>()); Timer timer = new Timer(); @Override public void contextInitialized(ServletContextEvent event) { timer.schedule(new MyTimerTask(sessionList), 0, 1*1000); } @Override public void contextDestroyed(ServletContextEvent event) { //取消当前定时器 timer.cancel(); } @Override public void sessionCreated(HttpSessionEvent event) { HttpSession session = event.getSession(); synchronized (sessionList) { sessionList.add(session); } System.out.println("Session产生:" + session.hashCode() + " : " + new Date().toLocaleString() ); } @Override public void sessionDestroyed(HttpSessionEvent event) { HttpSession session = event.getSession(); System.out.println("Session销毁:" + session.hashCode() + " : " + new Date().toLocaleString()); } } class MyTimerTask extends TimerTask { public MyTimerTask(List<HttpSession> sessionList) { this.list = sessionList; } @Override public void run() { synchronized (list) { ListIterator<HttpSession> listIt = list.listIterator(); while(listIt.hasNext()) { HttpSession session = listIt.next(); //测试HttpSession的存在时间 int middle = (int) ( System.currentTimeMillis() - session.getLastAccessedTime() ) / 1000; if (middle > 60 ) { //从list中移除当前HttpSession元素 listIt.remove(); //使得此session失效 session.invalidate(); } } } } }
e)HttpSessionBindListener监听器,专用于监听JavaBean对象在HttpSession中的状态情况:
检测JavaBean在Session中的状态,感知自己何时绑定在HttpSession中,何时从HttpSession中移除。
code:
//listener public class User implements HttpSessionBindingListener { @Override public void valueBound(HttpSessionBindingEvent event) { System.out.println("valueBound==>" + event.getName() + " : " + event.getValue()); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("valueUnbound==>" + event.getName() + " : " + event.getValue()); } } //java session.setAttribute("user", new User()); try { Thread.sleep(10000); } catch (Exception e) { } session.removeAttribute("user");
运行结果:
valueBound==>user : listener.domain.User@5f5220ee
valueUnbound==>user : listener.domain.User@5f5220ee
f)HttpSessionActivationListener监听器,专用于监听JavaBean对象的钝化与激活:
钝化与激活概念:
1)当HttpSession不用时,但用在HttpSession的有效期中,这时将内存中的HttpSessio移到外存,叫钝化。
2)反之,将HttpSession由外存移到内存,叫激活。
JavaBean对象,感知自己何时被钝化,合适被激活,由web容器决定。
配置钝化激活时间:
针对所有项目:在tomcat的conf/context.xml进行配置.
针对单个项目:只需在此项目的META-INF/加入一个context.xml文件即可。
配置内容如下:
<?xml version="1.0" encoding="utf-8"?> <Context> <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1"> //设置session钝化时间为1分钟 <Store className="org.apache.catalina.session.FileStore" directory="target" /> //设置钝化文件目标存放目录 </Manager> </Context>
默认钝化文件.session的目标文件夹为: tomcat的work目录下。
code:
public class Stu implements HttpSessionActivationListener { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public void sessionDidActivate(HttpSessionEvent event) { System.out.println("sessionDidActivate"); } @Override public void sessionWillPassivate(HttpSessionEvent event) { System.out.println("sessionWillPassivate"); } }
注意: 但凡是监听三个域对象,就需要在web.xml文件中配置,除外,无需要web.xml文件配置。
所以:
HttpSessionBindListener和HttpSessionActivationListener监听器无需在web.xml中进行配置。
4 监听器的工作过程和生命周期
开发过程:
a)写一个普通类实现对应的接口,即事件监听器
b)在web.xml文件中注册事件监听器
<!-- 事件源注册事件监听器,由容器完成 -->
<listener>
<listener-class>cn.itcast.web.listener.MyServletContextListener</listener-class>
</listener>
生命周期:
*空参构造(1次)->初始化(1次)->销毁化(1次),是一个单例的模式
在部署web应用是产生,即用户第一次访问之前已经产生,在重新部署web应用时,后销毁原监听器,再产生新的监听器
*5 案例
a)java.util.Timer定时器是以后台线程方式控制运行,它是线程安全,无需手工加锁
b)timer.schedule(new MyTimerTask(),0,5000);固定频度执行
c)Calendar c = Calendar.getInstance();
c.set(2011,10,6,10,30,40);
timer.schedule(new MyTimerTask(),c.getTime());指定时间执行
d)***Serlvet,Filter和Listener一样具有线程安全问题
e)当HttpSession过期时,Web容器负付执行对应的销毁方法,但是时间不精确
f)**Listener在Jsp或Servlet之前被执行
g)在线人数使用:context,第N个访问者使用:session
*补充:Timer类 ———— 定时器(线程安全的)
补充:timer.cancel() 方法,可以终止Timer定时器
public class TimerDemo { public static void main(String[] args) { Timer timer = new Timer(); //延迟5秒进行,并每隔1秒执行一次任务 //timer.schedule(new MyTimerTask(), 5000, 1000); //在2015年1月12日15时34分50秒执行一次任务 Calendar calendar = Calendar.getInstance(); calendar.set(2015, 0, 12, 15, 34, 50); timer.schedule(new MyTimerTask(), calendar.getTime()); } } class MyTimerTask extends TimerTask { @Override public void run() { System.out.println(new Date().toLocaleString()); } }