监听器
监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
Servlet监听器
在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为 ServletContext, HttpSession 和 ServletRequest 这三个域对象。
Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型。
- 监听三个域对象创建和销毁的事件监听器
- 监听域对象中属性的增加和删除的事件监听器
- 监听绑定到 HttpSession 域中的某个对象的状态的事件监听器。
编写监听器
和编写其它事件监听器一样,编写servlet监听器也需要实现一个特定的接口,并针对相应动作覆盖接口中的相应方法。
和其它事件监听器略有不同的是,servlet监听器的注册不是直接注册在事件源上,而是由WEB容器负责注册,开发人员只需在web.xml文件中使用<listener>标签配置好监听器,web容器就会自动把监听器注册到事件源中。
一个 web.xml 文件中可以配置多个 Servlet 事件监听器,web 服务器按照它们在 web.xml 文件中的注册顺序来加载和注册这些 Serlvet 事件监听器。
ServletContext监听器
ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件。
当 ServletContext 对象被创建时,激发contextInitialized (ServletContextEvent sce)方法
当 ServletContext 对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。
servletContext域对象何时创建和销毁:
- 创建:服务器启动针对每一个web应用创建servletcontext
- 销毁:服务器关闭前先关闭代表每一个web应用的servletContext
package com.hanqi.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class TestApplicationListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent arg0) { System.out.println("application对象销毁"); } @Override public void contextInitialized(ServletContextEvent arg0) { System.out.println("创建application对象"); } }
访问时会在控制台打印“创建application对象”
HttpSession监听器
HttpSessionListener接口用于监听HttpSession的创建和销毁
创建一个Session时,sessionCreated(HttpSessionEvent se) 方法将会被调用。
销毁一个Session时,sessionDestroyed (HttpSessionEvent se) 方法将会被调用。
Session域对象创建和销毁的时机创建:用户每一次访问时,服务器创建session
- 销毁:如果用户的session 30分钟没有使用,服务器就会销毁session,我们在web.xml里面也可以配置session失效时间
ServletRequest 监听器
ServletRequestListener 接口用于监听ServletRequest 对象的创建和销毁。
Request 对象被创建时,监听器的requestInitialized方法将会被调用。
Request对象被销毁时,监听器的requestDestroyed方法将会被调用。
servletRequest域对象创建和销毁的时机:
- 创建:用户每一次访问,都会创建一个reqeust
- 销毁:当前访问结束,request对象就会销毁
当向被监听器对象中增加一个属性时,web容器就调用事件监听器的 attributeAdded 方法进行相应,这个方法接受一个事件类型的参数,监听器可以通过这个参数来获得正在增加属性的域对象和被保存到域中的属性对象
各个域属性监听器中的完整语法定义为:
- public void attributeAdded(ServletContextAttributeEvent scae)
- public void attributeAdded (HttpSessionBindingEvent hsbe)
- public void attributeAdded(ServletRequestAttributeEvent srae)
监听三个域的属性变化
Servlet规范定义了监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。
这三个监听器接口分别是ServletContextAttributeListener, HttpSessionAttributeListener ServletRequestAttributeListener
这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。
当向被监听器对象中增加一个属性时,web容器就调用事件监听器的 attributeAdded 方法进行相应,这个方法接受一个事件类型的参数,监听器可以通过这个参数来获得正在增加属性的域对象和被保存到域中的属性对象
各个域属性监听器中的完整语法定义为:
- public void attributeAdded(ServletContextAttributeEvent scae)
- public void attributeAdded (HttpSessionBindingEvent hsbe)
- public void attributeAdded(ServletRequestAttributeEvent srae)
当删除被监听对象中的一个属性时,web 容器调用事件监听器的这个方法进行相应
各个域属性监听器中的完整语法定义为:
- public void attributeRemoved(ServletContextAttributeEvent scae)
- public void attributeRemoved (HttpSessionBindingEvent hsbe)
- public void attributeRemoved (ServletRequestAttributeEvent srae)
当监听器的域对象中的某个属性被替换时,web容器调用事件监听器的这个方法进行相应
各个域属性监听器中的完整语法定义为:
- public void attributeReplaced(ServletContextAttributeEvent scae)
- public void attributeReplaced (HttpSessionBindingEvent hsbe)
- public void attributeReplaced (ServletRequestAttributeEvent srae)
例子:
package com.hanqi.listener.attr; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; public class TestRequestAttributeListener implements ServletRequestAttributeListener { @Override public void attributeAdded(ServletRequestAttributeEvent arg0) { System.out.println("request对象添加属性"); } @Override public void attributeRemoved(ServletRequestAttributeEvent arg0) { System.out.println("移除request属性"); } @Override public void attributeReplaced(ServletRequestAttributeEvent arg0) { System.out.println("替换request属性"); } }
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <% request.setAttribute("hanqi", "淄博汉企");//设置request属性 request.removeAttribute("hanqi");移除request属性 session.getCreationTime();//获取session的创建时间 %> <h1>首页</h1> <hr> 当前在线人数: <%=application.getAttribute("userCount") %> <hr> 用户列表: User [sessionid, session创建时间] </body> </html>
应用:统计在线用户数量和用户信息
package com.util; import java.util.Date; public class User { private String sid; private Date stime; public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public Date getStime() { return stime; } public void setStime(Date stime) { this.stime = stime; } @Override public String toString() { return "User :sid=" + sid + ", stime=" + stime + "|"; } }
package com.listener; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import com.util.User; /** * Application Lifecycle Listener implementation class SessionListener * */ @WebListener public class SessionListener implements HttpSessionListener { private int userCount;//记录在线人数 private ArrayList<User> userList;//记录在线人的信息 /** * Default constructor. */ public SessionListener() { // TODO Auto-generated constructor stub } /** * @see HttpSessionListener#sessionCreated(HttpSessionEvent) */ //session创建时的方法 public void sessionCreated(HttpSessionEvent sessionEvent) { // TODO Auto-generated method stub userCount++;//用户每一次访问时,服务器创建session,用户数量+1 if(userList==null) { userList = new ArrayList<User>(); } //获取HttpSession,便于显示,不用每一次都调用 HttpSession session = sessionEvent.getSession(); String sessionid = session.getId();//获取唯一ID long stime = session.getCreationTime();//获取创建时间 //调用方法查看是不是已经存在的用户,如果不存在,新建用户存入信息,加到集合中 if(!checkUsersSession(sessionid, userList)) { User u = new User(); u.setStime(new Date(stime)); u.setSid(sessionid); userList.add(u); } //设置session属性,在线人数和在线信息 session.getServletContext().setAttribute("userCount", userCount); session.getServletContext().setAttribute("userList", userList); } private boolean checkUsersSession(String sessionid, ArrayList<User> userList2) { //查看是不是已经存在的用户,遍历集合,如果集合中有某个元素的id等于传入的id,返回true for(User u : userList2) { if(u.getSid().equals(sessionid)) { return true; } } return false; } /** * @see HttpSessionListener#sessionDestroyed(HttpSessionEvent) */ //session销毁时的方法 public void sessionDestroyed(HttpSessionEvent sessionEvent) { //用户下线,人数-1 userCount--; HttpSession session = sessionEvent.getSession(); String sessionid = session.getId(); System.out.println(sessionid + "的用户下线"); //获取销毁的用户的sessionid,在从集合中找到,移除 if(checkUsersSession(sessionid, userList)) { Iterator<User> iter = userList.iterator(); while(iter.hasNext()) { User u = iter.next(); if(u.getSid().equals(sessionid)) { iter.remove(); } } } session.getServletContext().setAttribute("userCount", userCount); session.getServletContext().setAttribute("userList", userList); } }
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" import="java.util.ArrayList,com.util.User"%> <%@ page import="java.util.*" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <body> 当前在线人数: <%=application.getAttribute("userCount") %> <hr> 在线用户列表:<br> <% List<User> userList = (List<User>)application.getAttribute("userList");//获取application对象,用户信息集合 if(userList!=null) {//如果集合不为空,遍历显示 for(User u : userList) { out.print(u+"<br>"); } } %> </body> </html>
尝试加一个踢人推出的功能