Skip to content

15. 监听器

在Web容器运行过程中,有很多关键点事件,比如Web应用被启动、用户会话开始、用户会话结束、用户请求到达等,Servlet API提供了大量监听器接口来帮助开发者实现对Web应用内特定事件进行监听,从而当Web应用内这些特定事件发生时,回调监听器内的事件监听方法来实现一些特殊功能,监听器的作用是监听Web容器的有效期事件,因此它是由容器管理的。

监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。

  • 在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为 ServletContext, HttpSession 和 ServletRequest 这三个域对象。

  • Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型。

    • 监听三个域对象创建和销毁的事件监听器
    • 监听域对象中属性的增加和删除的事件监听器
    • 监听绑定到 HttpSession 域中的某个对象的状态的事件监听器。
  • 下表列出了Servlet中的8个Listener接口和6个Event类。

Listener 接口Event 类
ServletContextListenerServletContextEvent
ServletContextAttributeListenerServletContextAttributeEvent
HttpSessionListenerHttpSessionEvent
HttpSessionActivationListener
HttpSessionAttributeListenerHttpSessionBindingEvent
HttpSessionBindingListener
ServletRequestListenerServletRequestEvent
ServletRequestAttributeListenerServletRequestAttributeEvent

15.1 监听器创建与使用

监听器的实现通过两个步骤完成:

步骤一:定义监听器实现类,实现监听器接口的所有方法;

步骤二:通过Annotation或在web.xml文件中声明Listener。

​ 注解@WebListener用于对监听器进行声明。

@WebListener
public class MyServletContextListener implements ServletContextListener {

​ 与其等价的web.xml中的声明形式如下

xml
<listener>
	<listener-class>com.neuedu.listener. MyServletContextListener </listener-class>
</listener>

15.2 Servlet上下文监听器

与Servlet上下文相关的监听器需要实现的监听器接口

监听器接口名称说明
ServletContextListener用于监听ServletContext(application)对象的创建和销毁
ServletContextAttributeListener用于监听ServletContext(application)范围内属性的改变
  • ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件。

    • 在ServletContextListener接口中定义了如下两个事件处理方法:
      • contextInitialized(ServletContextEvent sce)
        • 当ServletContext对象被创建时,Web容器将调用此方法。
      • contextDestroyed(ServletContextEvent sce)
        • 当ServletContext对象被销毁时,Web容器调用此方法。
  • ServletContext域对象创建和销毁:

    • 创建:服务器启动针对每一个web应用创建servletcontext
    • 销毁:服务器关闭前先关闭代表每一个web应用的servletContext

实例代码

java
@WebListener
public class MyApplicationListener implements ServletContextListener {


    Connection conn = null;
    @Override
    public void contextInitialized(ServletContextEvent sce) {

        Object object = sce.getSource();
        ServletContext application = sce.getServletContext();
        String realPath = application.getRealPath("/");

        System.out.println("应用被初始化,MyApplicationListener.contextInitialized");
        System.out.println("应用发布的路径 :"+realPath);

    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("应用被销毁: MyApplicationListener.contextDestroyed");
    }
}

15.3 HttpSession监听器

  • HttpSessionListener接口用于监听HttpSession的创建和销毁

  • 在HttpSessionListener接口中定义了如下两个事件处理方法:

    • sessionCreated(HttpSessionEvent se)
      • 当HttpSession对象被创建时,Web容器将调用此方法。该方法接收HttpSessionEvent事件对象,通过此对象可获得当前被创建的HttpSession对象;
    • sessionDestroyed(HttpSessionEvent se)
      • 当HttpSession对象被销毁时,Web容器调用此方法,同时向其传递HttpSessionEvent事件对象。

应用案例:当前在线人数的统计功能

实例代码

15.3.1 MySessionListner

java
package com.neuedu.listener;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.ArrayList;
import java.util.List;

/**
 * 项目:      CrossDomain
 * 类名:       MySessionListner
 * 创建时间:  2020/12/17  15:58
 * 描述 :     会话监听
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
public class MySessionListner implements HttpSessionListener {

    //当前登录的会话
    private List<HttpSession> sessionList = new ArrayList();



    @Override
    public void sessionCreated(HttpSessionEvent se) {
        ServletContext app = se.getSession().getServletContext();
        app.setAttribute("sessionList",sessionList);



        System.out.println("session创建: MySessionListner.sessionCreated");
        sessionList.add(se.getSession());
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("session销毁: MySessionListner.sessionDestroyed");
        sessionList.remove(se.getSession());
    }
}

在web.xml中声明监听器

xml
  <listener>
    <listener-class>com.neuedu.listener.MySessionListner</listener-class>
  </listener>

15.3.2 session-user-list.jsp

html
<%@ page import="java.util.List" %><%--
  Created by IntelliJ IDEA.
  User: root
  Date: 2020/12/17
  Time: 16:07
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>


<%
    List<HttpSession> sessionList = (List<HttpSession>) application.getAttribute("sessionList");

%>
<h3>当前在线人数:<%=sessionList.size()%></h3>

<table cellpadding="0" cellspacing="0" border="1" width="90%">

    <tr>
        <td>序号</td>
        <td>登录时间</td>

    </tr>

    <%
        int index = 0;
        for (HttpSession httpSession : sessionList) {
    %>
    <tr>
        <td><%=++index%></td>
        <td><%=httpSession.getCreationTime()%></td>
    </tr>

    <%
        }
    %>
    
</table>


</body>
</html>

访问效果:

15.3.3 invalidate.jsp

html
<%--
  Created by IntelliJ IDEA.
  User: root
  Date: 2020/12/17
  Time: 16:02
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%
    session.invalidate();
%>

</body>
</html>

15.4 HttpRequest监听器

  • ServletRequestListener 接口用于监听ServletRequest 对象的创建和销毁。

  • ServletRequestListener接口中定义了如下两个事件处理方法:

    • requestInitialized(ServletRequestEvent sre)
      • 当ServletRequest对象被创建时,Web容器将调用此方法。该方法接收ServletRequestEvent事件对象,通过此对象可获得当前被创建的ServletRequest对象;
    • requestDestroyed(ServletRequestEvent sre)
      • 当ServletRequest对象被销毁时,Web容器调用此方法,同时向其传递ServletRequestEvent事件对象。
    java
    package com.neuedu.listener;
    
    import javax.servlet.ServletRequestEvent;
    import javax.servlet.ServletRequestListener;
    import javax.servlet.annotation.WebListener;
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 项目:      CrossDomain
     * 类名:       MyServletRequestListener
     * 创建时间:  2020/12/17  16:40
     * 描述 :
     * 作者 :     张金山
     * QQ :     314649444
     * Site:      https://jshand.gitee.io
     */
    @WebListener
    public class MyServletRequestListener implements ServletRequestListener {
        @Override
        public void requestDestroyed(ServletRequestEvent sre) {
            System.out.println("请求对象被销毁:MyServletRequestListener.requestDestroyed");
        }
    
        @Override
        public void requestInitialized(ServletRequestEvent sre) {
    
            HttpServletRequest  request = (HttpServletRequest) sre.getServletRequest();
            System.out.println("请求对象被创建:MyServletRequestListener.requestInitialized");
            System.out.println("请求:路径:"+request.getRequestURL().toString());
            System.out.println("请求:路径:"+request.getRequestURI());
    
        }
    }

15.5 监听三个域对象属性变化

Servlet规范定义了监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。

  • 这三个监听器接口分别是
    • ServletContextAttributeListener,
    • HttpSessionAttributeListener
    • ServletRequestAttributeListener
  • 这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。
  • 方法示例:
    • public void attributeAdded(ServletContextAttributeEvent scae)
    • public void attributeReplaced(HttpSessionBindingEvent hsbe)
    • public void attributeRmoved(ServletRequestAttributeEvent srae)
java
package com.neuedu.listener;

import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

/**
 * 项目:      CrossDomain
 * 类名:       MyAttributeListener
 * 创建时间:  2020/12/17  16:51
 * 描述 :    监听域对象 属性的变化  attribute
 * 作者 :     张金山
 * QQ :     314649444
 * Site:      https://jshand.gitee.io
 */
@WebListener
public class MyAttributeListener implements ServletContextAttributeListener , HttpSessionAttributeListener , ServletRequestAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent event) {
        ServletContext app = event.getServletContext();


        String attributeName = event.getName();
        Object value = event.getValue();

        System.out.println(String.format("\r\n属性添加 name: %s\t value :%s ",attributeName,value.toString()));
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent event) {
        String attributeName = event.getName();
        Object value = event.getValue();

        System.out.println(String.format("\r\n属性 remove name: %s\t value :%s ",attributeName,value.toString()));
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent event) {
        String attributeName = event.getName();
        Object oldValue = event.getValue(); //zs
        Object value = event.getServletContext().getAttribute(attributeName); //li

        System.out.println(String.format("\r\n属性replace name: %s\t value :%s \t newValue:%s",attributeName,oldValue.toString(), value.toString()));
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {

    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {

    }

    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {

    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {

    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {

    }
}

15.6 Session绑定的事件监听器

保存在 Session 域中的对象可以有多种状态:

  1. 绑定到 Session 中;
  2. 从 Session 域中解除绑定;
  3. 随 Session 对象持久化到一个存储设备中(钝化);
    • 钝化是指服务器会将不常使用的Session对象暂时序列化到系统文件或数据库中。
  4. 随 Session 对象从一个存储设备中恢复(活化)
    • 活化就是将暂存在系统文件或数据库中的Session对象反序列化到服务器中,当Tomcat服务器被关闭或者重启时,Tomcat会将Session对象钝化到服务器文件系统中,当服务器被重新加载时,Session对象也会被活化。
  • Servlet 规范中定义了两个特殊的监听器接口来帮助 JavaBean 对象了解自己在 Session 域中的这些状态
  • HttpSessionBindingListener接口
  • HttpSessionActivationListener接口

这一类监听器在被定义之后不需要在进行声明注册

实现了HttpSessionBindingListener接口的 JavaBean 对象可以感知自己被绑定到 Session 中和从 Session 中删除的事件

  • 当对象被绑定到 HttpSession 对象中时,web 服务器调用该对象的 void valueBound(HttpSessionBindingEvent event) 方法

  • 当对象从 HttpSession 对象中解除绑定时,web 服务器调用该对象的 void valueUnbound(HttpSessionBindingEvent event)方法

这里绑定对象与解除绑定使用的仍然是Session的setAttribute和removeAttribute方法,这和前面设置属性是一样的,那么这种绑定对象的监听器与设置属性的监听器有什么区别呢?绑定对象的监听器只监听某种类型的对象的绑定与解绑操作,而设置属性的监听器监听的是所有设置属性的动作。

Released under the MIT License.