监听器Listener应用
监听器是指专门用于在其他对象身上发生的事件或者状态改变进行监听和相应处理的对象,当被监听的对象发生变化时立即采取相应的行动
Web监听器定义:
- Servlet规范中定义的一种特殊类
- 用于监听ServletContext、HttpSession、ServletRequest等域对象的创建与销毁事件
- 用于监听ServletContext、HttpSession、ServletRequest域对象的属性attribute修改的事件
- 监听事件的类型是通过类所实现的接口进行区分
- 可以在事件发生前和发生后作出一些必要的处理
application对象创建和销毁的监听
- 一个应用对应一个application对象
- 应用启动后自动创建application对象,关闭应用前自动销毁application对象
1、定义一个类实现ServletContextListener
public class CatalogListener implements ServletContextListener {
//在application对象创建时自动回调执行
public void contextInitialized(ServletContextEvent sce) {
ServletContext application=sce.getServletContext();//获取触发事件执行的事件源
//模拟数据的加载
List<CatalogBean> clist=new ArrayList<>();
for(int i=0;i<5;i++){
CatalogBean tmp=new CatalogBean();
tmp.setId(1L+i);
tmp.setName("name"+i);
clist.add(tmp);
}
application.setAttribute("catalogList",clist);
}
//在application对象销毁时自动回调执行
public void contextDestroyed(ServletContextEvent sce) {
ServletContext application=sce.getServletContext();
application.removeAttribute("catalogList");
}
}
2、在web.xml中进行配置
<listener>
<listener-class>com.yan.listeners.CatalogListener</listener-class>
</listener>
3、当特定事件产生时由服务器自动调用
监听的对象
监听的对象有:ServletContext、HttpSession、ServletRequest分别对应JSP9大内置对象中的application、session和request。用于监听ServletContext、HttpSession、ServletRequest等域对象的创建和销毁事件,监听域对象属性发生修改的事件。监听器对象可以在事件发生前、发生后做一些必要的处理。
- application上下文对象,是由容器创建和初始化的,它的范围是整个的web应用,而且整个web应用中只会有一个 application对象存在,当web服务器关闭时,就会将application对象销毁
- session是一种保存用户上下文信息的机制,它是针对每个会话的,它是放在服务器端,通过SessionId区分的,在浏览器和服务器结束后,都会将session销毁。一个用户对应一个session,当长时间不使用会超时自动销毁
- request对象是当客户端发送请求时,容器就会创建一个ServletRequest对象,来进行封装请求数据,同时会创建一个servletResponse对象来进行封装相应数据,当结束封装请求之后,就会销毁该对象 WEB监听器,就是监听这3个对象的创建、销毁和它们的属性发生的变化
监听事件
事件类型 | 描述 | 接口 |
---|---|---|
ServletContext | 处理Servlet上下文被初始化或者被销毁的事件 | ServletContextListener |
ServletContext | 处理Servlet上下文内的属性被增加、删除或者替换时发生的事件 | ServletContextAttributeListener |
HttpSession | 处理HttpSession被初始化或者被销毁的事件 | HttpSessionListener |
HttpSession | 处理HttpSession被激活或钝化时发生的事件 | HttpSessionActivationListener |
HttpSession | 处理HttpSession内的属性被增加、删除或者替换时发生的事件 | HttpSessionAttributeListener |
HttpSession | 处理对象被绑定或者移出HttpSession发生的事件 | HttpSessionBindingListener |
ServletRequest | 处理ServletRequest请求被初始化或者被销毁的事件 | ServletRequestListener |
ServletRequest | 处理ServletRequest请求内的属性被增加、删除或者替换时发生的事件 | ServletRequestAttributeListener |
监听创建和销毁
ServletContextListener用于监听application对象
主要用途是做一些定时器、加载一些全局属性对象、创建全局的数据库连接、加载一些缓存信息
public interface ServletContextListener extends EventListener {
default public void contextInitialized(ServletContextEvent sce) {}
default public void contextDestroyed(ServletContextEvent sce) {}
}
HttpSessionListener用于监听session对象
该监听器的主要作用是统计在线人数、记录访问日志【在后台统计访问时间、IP信息做一些统计数据】
public interface HttpSessionListener extends EventListener {
default public void sessionCreated(HttpSessionEvent se) {} //考点:方法名称
default public void sessionDestroyed(HttpSessionEvent se) {}
}
ServletRequestListener用于监听request对象
该监听器的主要作用是:读取参数、记录访问历史。
public interface ServletRequestListener extends EventListener {
default public void requestDestroyed(ServletRequestEvent sre) {}
default public void requestInitialized(ServletRequestEvent sre) {}
}
监听attribute的增删改
ServletContexAttributetListener用于监听application对象
public interface ServletContextAttributeListener extends EventListener {
//新增attribute时回调
default public void attributeAdded(ServletContextAttributeEvent event) {}
//删除attribute后回调
default public void attributeRemoved(ServletContextAttributeEvent event) {}
//替换attribute后回调
default public void attributeReplaced(ServletContextAttributeEvent event) {}
}
HttpSessionAttributeListener用于监听session对象
public interface HttpSessionAttributeListener extends EventListener {
default public void attributeAdded(HttpSessionBindingEvent event) {} //事件对象的命名不一致
default public void attributeRemoved(HttpSessionBindingEvent event) {}
default public void attributeReplaced(HttpSessionBindingEvent event) {}
}
ServletRequestAttributeListener用于监听request对象
public interface ServletRequestAttributeListener extends EventListener {
default public void attributeAdded(ServletRequestAttributeEvent srae) {}
default public void attributeRemoved(ServletRequestAttributeEvent srae) {}
default public void attributeReplaced(ServletRequestAttributeEvent srae) {}
}
统计在线人数
- 统计的人数值为了实现跨用户数据共享,所以只能使用application存储
- 通过监听session对象创建和销毁计算在线人数
public class CounterListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) { //创建session对象后给application中存储的计数加1
Long counter=0L;
HttpSession session=se.getSession();
ServletContext application=session.getServletContext();
Object obj=application.getAttribute("counter");
if(obj!=null && obj instanceof Long)
counter=(Long)obj;
counter++;
application.setAttribute("counter",counter);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) { //销毁session对象时给application中存储的计数减1
Long counter=0L;
HttpSession session=se.getSession();
ServletContext application=session.getServletContext();
Object obj=application.getAttribute("counter");
if(obj!=null && obj instanceof Long)
counter=(Long)obj;
counter--;
if(counter<0)
counter=0L;
application.setAttribute("counter",counter);
}
}
配置 web.xml
<listener>
<listener-class>com.yan.listeners.CounterListener</listener-class>
</listener>
在页面上获取计数值
${applicationScope.counter}
练习: 统计网站从发布之日起的访问人数
- 不是点击量
- 要求可以配置初始值
- 要求关闭服务器时记录累加值
public class CounterListener implements HttpSessionListener, ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext application=sce.getServletContext();
InputStream is=application.getResourceAsStream("counter.data"); //读取数据文件
DataInputStream dis=new DataInputStream(is);
Long l2=null;
try {
l2 = dis.readLong();
} catch (Exception e){
l2=0L;
}
String ss=application.getInitParameter("counter"); //读取上下文参数
Long l1=null;
try{
l1=Long.parseLong(ss);
} catch (Exception e){
l1=0L;
}
Long counter=Math.max(l1,l2);
application.setAttribute("counter",counter);
}
//线程可能不安全,但是使用同步处理会影响并发性
public void sessionCreated(HttpSessionEvent se) {
Long counter=0L;
HttpSession session=se.getSession();
ServletContext application=session.getServletContext();
Object obj=application.getAttribute("counter");
if(obj!=null && obj instanceof Long)
counter=(Long)obj;
counter++;
application.setAttribute("counter",counter);
}
//线程安全
public void contextDestroyed(ServletContextEvent sce) {
ServletContext application=sce.getServletContext();
Long counter=0L;
Object obj=application.getAttribute("counter");
if(obj!=null && obj instanceof Long)
counter=(Long)obj;
String ss=application.getRealPath(""); //获取应用部署后的根路径
File ff=new File(ss,"counter.data");
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(ff))){
dos.writeLong(counter);
}catch (Exception e){
application.log(e.getMessage());
}
}
}
web监听器用途:
-
统计在线人数和在线用户
登录成功的时候 session.setAttribute(“user”,user);
public class UserListener implements HttpSessionAttributeListener{ @Override public void attributeAdded(HttpSessionBindingEvent event) { String name=event.getName(); Object obj=event.getValue(); System.out.println("name:"+name+",value:"+obj); if("user".equals(name)) { HttpSession session = event.getSession(); ServletContext application = session.getServletContext(); Set<UserBean> userSet = new HashSet<>(); Object obj2 = application.getAttribute("userset"); if (obj2 != null && obj2 instanceof Set) userSet = (Set<UserBean>) obj2; userSet.add(obj); application.setAttribute("userset", userSet); } }
-
系统启动时加载初始化信息
-
统计网站访问量
-
和Spring整合
监听器的启动顺序:
- 在一个web.xml中可以注册多个Servlet监听器,监听器的加载顺序是按照web.xml文件中的顺序加载的
- 如果在web.xml文件中同时配置了监听器、过滤器和Servlet,那么它们的优先级是:
- 监听器>过滤器>Servlet
HttpSession的特殊操作
一般情况下session是保存在服务器内存的,服务器为每一个在线用户保存session对象,当在线用户很多时,session内存的开销将是非常巨大的——直接导致web服务器性能的降低。session的钝化机制就是将不经常使用的session对象序列化存储到文件系统或者是数据库中。需要使用这些session的时候进行反序列化到内存中。整个过程由服务器自动完成
session钝化机制一般是由容器提供的SessionManger管理
-
Tomcat针对session的管理默认采用LRU策略,最近最少使用原则,
-
当Tomcat服务器被关闭或重启时,Tomcat服务器会将当前内存中的Session对象钝化到服务器的文件系统中
-
Web应用程序被重新加载(修改了web.xml)时,内存中的Session对象也会被钝化到服务器的文件系统中
-
可以配置主流内存的Session对象数目,将不常使用的session对象保存到文件系统或者数据库,用的时候重新加载。
-
钝化后的文件的位置:Tomcat安装路径/work/Catalina/hostname/applicationname/SESSIONS.ser
//具体session的激活和钝化是由服务器负责实现的,具体的实现方法是采用对象流,所以要求存储在session中的数据必须实现了Serializable接口
public interface HttpSessionActivationListener extends EventListener {
default public void sessionWillPassivate(HttpSessionEvent se) {}//钝化前执行的操作,可以关闭一些不能钝化的内容
default public void sessionDidActivate(HttpSessionEvent se) {} //激活后执行的操作,可以用于执行一些恢复操作
}
在Servlet规范中提供了两个接口HttpSessionBindingListener和HttpSessionActivationListener分别对应绑定-解除绑定方法和钝化-活化方法。这两个监听器不需要在web.xml中注册。
public class User implements HttpSessionBindingListener {
private String username;
private String password;
public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("绑定,属性名:"+ httpSessionBindingEvent.getName());
}
public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
System.out.println("解除绑定,属性名:"+ httpSessionBindingEvent.getName());
}
}