Servlet - Listeners

1. What

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

监听器的相关概念

  • 事件(Events ):方法调用、属性改变、状态改变等。
  • 事件源(Event source):被监听的对象
  • 监听器(Listener ):用于监听事件源对象 ,事件源对象状态的变化都会触发监听器。
  • 注册监听器(Register listener):将监听器与事件源进行绑定。

Servlet 规范中定义了 8 个监听器接口,可以用于监听 ServletContext、HttpSession 和 ServletRequest 对象的生命周期和属性变化事件。开发 Servlet 监听器需要实现相应的监听器接口并重写接口中的方法。

监听器 Listener 按照监听的事件划分,可以分为 3 类

  • Listeners for object creation and destruction(监听对象创建和销毁的监听器)
  • Listeners that listens for property changes in an object(监听对象中属性变更的监听器)
  • Listeners that listens for changes in the state of objects in HttpSession(监听 HttpSession 中的对象状态改变的监听器)

监听对象创建和销毁的监听器

Servlet 规范定义了监听 ServletContext、HttpSession、HttpServletRequest 这三个对象创建和销毁事件的监听器,如下表所示。

event source (事件源)listener(监听器)description(监听器描述)create or destory method(创建和销毁方法)
ServletContextServletContextListener用于监听 ServletContext 对象的创建过程void contextInitialized (ServletContextEvent sce)
ServletContextServletContextListener用于监听 ServletContext 对象的销毁过程void contextDestroyed (ServletContextEvent sce)
HttpSessionHttpSessionListener用于监听 HttpSession 对象的创建过程void sessionCreated (HttpSessionEvent se)
HttpSessionHttpSessionListener用于监听 HttpSession 对象的销毁过程void sessionDestroyed (HttpSessionEvent se)
ServletRequestServletRequestListener用于监听 ServletRequest 对象的创建过程void requestInitialized (ServletRequestEvent sre)
ServletRequestServletRequestListener用于监听 ServletRequest 对象的销毁过程void requestDestroyed (ServletRequestEvent sre)

监听对象中属性变更的监听器

Servlet 规范定义了监听 ServletContext、HttpSession、HttpServletRequest 这三个对象中的属性变更事件的监听器,这三个监听器接口分别是 ServletContextAttributeListener、HttpSessionAttributeListener 和 ServletRequestAttributeListener。这三个接口中都定义了三个方法,用来处理被监听对象中属性的增加,删除和替换事件。同一种事件在这三个接口中对应的方法名称完全相同,只是参数类型不同,如下表所示。

event source (事件源)listener(监听器)description(监听器描述)method(方法)
ServletContextServletContextAttributeListener用于监听 ServletContext 对象的属性新增public void attributeAdded (ServletContextAttributeEvent scae)
ServletContextServletContextAttributeListener用于监听 ServletContext 对象的属性移除public void attributeRemoved (ServletContextAttributeEvent scae)
ServletContextServletContextAttributeListener用于监听 ServletContext 对象的属性替换public void attributeReplaced (ServletContextAttributeEvent scae)
HttpSessionHttpSessionAttributeListener用于监听 ServletContext 对象的属性新增public void attributeAdded (HttpSessionBindingEvent hsbe)
HttpSessionHttpSessionAttributeListener用于监听 ServletContext 对象的属性移除public void attributeRemoved (HttpSessionBindingEvent hsbe)
HttpSessionHttpSessionAttributeListener用于监听 ServletContext 对象的属性替换public void attributeReplaced (HttpSessionBindingEvent hsbe)
HttpServletRequestServletRequestAttributeListener用于监听 ServletContext 对象的属性新增public void attributeAdded (ServletRequestAttributeEvent srae)
HttpServletRequestServletRequestAttributeListener用于监听 ServletContext 对象的属性移除public void attributeRemoved (ServletRequestAttributeEvent srae)
HttpServletRequestServletRequestAttributeListener用于监听 ServletContext 对象的属性替换public void attributeReplaced (ServletRequestAttributeEvent srae)

监听 HttpSession 中的对象状态改变的监听器

Session 中的对象可以有多种状态:绑定到 Session 中、从 Session 中解除绑定、随 Session 对象持久化到存储设备中(钝化)、随 Session 对象从存储设备中恢复(活化)。
Servlet 规范中定义了两个特殊的监听器接口,用来帮助对象了解自己在 Session 中的状态:HttpSessionBindingListener 接口和 HttpSessionActivationListener 接口 ,实现这两个接口的类不需要进行注册。

event source (事件源)listener(监听器)description(监听器描述)method(方法)
HttpSessionHttpSessionBindingListener用于监听 JavaBean 对象绑定到 HttpSession 对象的事件void valueBound (HttpSessionBindingEvent event)
HttpSessionHttpSessionBindingListener用于监听 JavaBean 对象从 HttpSession 对象解绑的事件void valueUnbound (HttpSessionBindingEvent event)
HttpSessionHttpSessionActivationListener用于监听 HttpSession 中对象活化的过程void sessionWillPassivate (HttpSessionBindingEvent event)
HttpSessionHttpSessionActivationListener用于监听 HttpSession 中对象钝化的过程void sessionDidActive (HttpSessionBindingEvent event)

2. Why

  • Listener 我是这样理解他的,他是一种观察者模式的实现:我们在 web.xml 中配置 listener 的时候就是把一个被观察者放入的观察者的观察对象队列中,当被观察者触发了注册事件时观察者作出相应的反应。在 jsp/servlet 中具体的实现是在 web.xml 中注册 Listener ,由 Container 在特定事件发生时呼叫特定的实现 Listener 的类。
  • Listener mode VS observer mode:
    在这里插入图片描述

3. How

Servlet 监听器Listener 的执行顺序

Listener的启动优先级是大于过滤器的,即Listener>Filter>Servlet,如果有很多Listener类,那么Listener的作用时机是根据你在web.xml中注册的顺序来决定的,即按照从上往下的顺序来加载.

1.在web.xml中配置
2. 在 ServletContext 创建之前,Listener监听器(包括其他类型监听器)会先按配置顺序初始化;
3. 然后 ServletContext 初始化完成后会按照监听器配置的顺序回调相应的方法,比如 ServletContextListener 的 contextInitialized() 方法。

使用场景

  • 在系统启动时加载初始化信息
  • 统计网站的访问量
  • 统计在线人数和在线用户
  • 运用在一些框架(如Spring)中完成特定功能.

4. Sample

Servlet Listener config

我们可以使用@WebListener注解来声明一个类作为Listener,但是该类应该实现一个或多个Listener接口。
我们可以在web.xml中定义listener:

<listener>
    <listener-class>
        com.journaldev.listener.AppContextListener
    </listener-class>
</listener>

web.xml:在部署描述符中,我将定义一些上下文初始化参数和监听器配置。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>ServletListenerExample</display-name>
  
  <context-param>
    <param-name>DBUSER</param-name>
    <param-value>pankaj</param-value>
  </context-param>
  <context-param>
    <param-name>DBPWD</param-name>
    <param-value>password</param-value>
  </context-param>
  <context-param>
    <param-name>DBURL</param-name>
    <param-value>jdbc:mysql://localhost/mysql_db</param-value>
  </context-param>
  
  <listener>
    <listener-class>com.journaldev.listener.AppContextListener</listener-class>
  </listener>
  <listener>
    <listener-class>com.journaldev.listener.AppContextAttributeListener</listener-class>
  </listener>
  <listener>
    <listener-class>com.journaldev.listener.MySessionListener</listener-class>
  </listener>
  <listener>
    <listener-class>com.journaldev.listener.MyServletRequestListener</listener-class>
  </listener>
</web-app>

DBConnectionManager:这是数据库连接的类,为了简单起见,我没有为实际的数据库连接提供代码。将这个对象设置为servlet上下文的属性。

package com.journaldev.db;
import java.sql.Connection;

public class DBConnectionManager {

    private String dbURL;
    private String user;
    private String password;
    private Connection con;
    
    public DBConnectionManager(String url, String u, String p){
        this.dbURL=url;
        this.user=u;
        this.password=p;
        //create db connection now
        
    }
    
    public Connection getConnection(){
        return this.con;
    }
    
    public void closeConnection(){
        //close DB connection here
    }
}

MyServlet:一个简单的servlet类,将使用会话,属性等。

package com.journaldev.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
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 javax.servlet.http.HttpSession;

@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            ServletContext ctx = request.getServletContext();
            ctx.setAttribute("User", "Pankaj");
            String user = (String) ctx.getAttribute("User");
            ctx.removeAttribute("User");
            
            HttpSession session = request.getSession();
            session.invalidate();
            
            PrintWriter out = response.getWriter();
            out.write("Hi "+user);
    }
}

接下来将实现监听器类,为常用的监听器提供了示例侦听器类 —— ServletContextListener,ServletContextAttributeListener,ServletRequestListener和HttpSessionListener。

ServletContextListener:将读取servlet context init参数来创建DBConnectionManager对象,并将其设置为ServletContext对象的属性。

package com.journaldev.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import com.journaldev.db.DBConnectionManager;

@WebListener
public class AppContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ServletContext ctx = servletContextEvent.getServletContext();
        
        String url = ctx.getInitParameter("DBURL");
        String u = ctx.getInitParameter("DBUSER");
        String p = ctx.getInitParameter("DBPWD");
        
        //create database connection from init parameters and set it to context
        DBConnectionManager dbManager = new DBConnectionManager(url, u, p);
        ctx.setAttribute("DBManager", dbManager);
        System.out.println("Database connection initialized for Application.");
    }

    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        ServletContext ctx = servletContextEvent.getServletContext();
        DBConnectionManager dbManager = (DBConnectionManager) ctx.getAttribute("DBManager");
        dbManager.closeConnection();
        System.out.println("Database connection closed for Application."); 
    }
}

ServletContextAttributeListener:在servlet上下文中添加,删除或替换属性时,记录事件的简单实现。

package com.journaldev.listener;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class AppContextAttributeListener implements ServletContextAttributeListener {

    public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext attribute added::{"+servletContextAttributeEvent.getName()+","+servletContextAttributeEvent.getValue()+"}");
    }

    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext attribute replaced::{"+servletContextAttributeEvent.getName()+","+servletContextAttributeEvent.getValue()+"}");
    }

    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext attribute removed::{"+servletContextAttributeEvent.getName()+","+servletContextAttributeEvent.getValue()+"}");
    } 
}

HttpSessionListener:创建或销毁会话时记录事件的简单实现。

package com.journaldev.listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class MySessionListener implements HttpSessionListener {

    public void sessionCreated(HttpSessionEvent sessionEvent) {
        System.out.println("Session Created:: ID="+sessionEvent.getSession().getId());
    }

    public void sessionDestroyed(HttpSessionEvent sessionEvent) {
        System.out.println("Session Destroyed:: ID="+sessionEvent.getSession().getId());
    } 
}

ServletRequestListener:接口的简单实现,用于在请求初始化和销毁​​时记录ServletRequest IP地址。

package com.journaldev.listener;

import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyServletRequestListener implements ServletRequestListener {

    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        ServletRequest servletRequest = servletRequestEvent.getServletRequest();
        System.out.println("ServletRequest destroyed. Remote IP="+servletRequest.getRemoteAddr());
    }

    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        ServletRequest servletRequest = servletRequestEvent.getServletRequest();
        System.out.println("ServletRequest initialized. Remote IP="+servletRequest.getRemoteAddr());
    }  
}

现在,将使用URL部署我们的应用程序并在浏览器中访问MyServlet时http://localhost:8080/ServletListenerExample/MyServlet,则将在服务器日志文件中看到以下日志。

ServletContext attribute added::{DBManager,com.journaldev.db.DBConnectionManager@4def3d1b}
Database connection initialized for Application.
ServletContext attribute added::{org.apache.jasper.compiler.TldLocationsCache,org.apache.jasper.compiler.TldLocationsCache@1594df96}

ServletRequest initialized. Remote IP=0:0:0:0:0:0:0:1%0
ServletContext attribute added::{User,Pankaj}
ServletContext attribute removed::{User,Pankaj}
Session Created:: ID=8805E7AE4CCCF98AFD60142A6B300CD6
Session Destroyed:: ID=8805E7AE4CCCF98AFD60142A6B300CD6
ServletRequest destroyed. Remote IP=0:0:0:0:0:0:0:1%0


ServletRequest initialized. Remote IP=0:0:0:0:0:0:0:1%0
ServletContext attribute added::{User,Pankaj}
ServletContext attribute removed::{User,Pankaj}
Session Created:: ID=88A7A1388AB96F611840886012A4475F
Session Destroyed:: ID=88A7A1388AB96F611840886012A4475F
ServletRequest destroyed. Remote IP=0:0:0:0:0:0:0:1%0


Database connection closed for Application.

注意日志的顺序,它按照执行的顺序。当您关闭应用程序或关闭容器时,将显示最后一个日志。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是王小贱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值