老杜 JavaWeb 讲解(二十) ——Listener监听器

发布时间 2023-08-09 16:20:06作者: 猪无名

(十八)Listener监听器

引子:静态代码块

package com.zwm.javaweb.servlet;

/**
 * @author 猪无名
 * @date 2023/8/8 13 47
 * discription:
 */
public class test2 {
    // 静态代码块在类加载时执行,并且只执行一次。
    //这个语法很简单,但什么时候用?很疑惑

    //假如,你希望在类加载的时候运行一段代码,这时候就可以用到静态代码块,这是Java语言预留给程序员的一个时机(类加载时机)。
    //静态代码块实际上用的不多。
    static {
        System.out.println("类加载了。");
    }

    public static void main(String[] args) {

    }
}

监听器的作用就类似静态代码块,是预留给程序员的时机。

  • 什么是监听器?

    • 监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。
    • 在Servlet中,所有的监听器接口都是以“Listener”结尾。
  • 监听器有什么用?

    • 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。
    • 特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。
  • Servlet规范中提供了哪些监听器?

    • jakarta.servlet包下:
      • ServletContextListener
      • ServletContextAttributeListener
      • ServletRequestListener
      • ServletRequestAttributeListener
    • jakarta.servlet.http包下:
      • HttpSessionListener
      • HttpSessionAttributeListener
        • 该监听器需要使用@WebListener注解进行标注。
        • 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。
      • HttpSessionBindingListener
        • 该监听器不需要使用@WebListener进行标注。
        • 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。
        • 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。
      • HttpSessionIdListener
        • session的id发生改变的时候,监听器中的唯一一个方法就会被调用。
      • HttpSessionActivationListener
        • 监听session对象的钝化和活化的。
        • 钝化:session对象从内存存储到硬盘文件。
        • 活化:从硬盘文件把session恢复到内存。
  • 实现一个监听器的步骤:以ServletContextListener为例。

    • 第一步:编写一个类实现ServletContextListener接口。并且实现里面的方法。

      • public class Listener01 implements ServletContextListener {
            @Override
            public void contextInitialized(ServletContextEvent sce) {
                //这个方法是在ServletContext对象被创建的时候调用的。
                Sustem.out.println("ServletContext对象被创建的时候调用的。");
            }
        
            @Override
            public void contextDestroyed(ServletContextEvent sce) {
        		//这个方法是在ServletContext对象被销毁的时候调用的。
                 Sustem.out.println("ServletContext对象被销毁的时候调用的。");
            }
        }
        
    • 第二步:在web.xml文件中对ServletContextListener进行配置,如下:

      • <listener>
            <listener-class>com.zwm.javaweb.listener.Listener01</listener-class>
        </listener>
        
      • 当然,第二步也可以不使用配置文件,也可以用注解,例如:@WebListener

  • 注意:所有监听器中的方法都是不需要javaweb程序员手动调用的,由服务器来负责调用?什么时候被调用呢?

    • 当某个特殊的事件发生(特殊的事件发生其实就是某个时机到了。)之后,被web服务器自动调用。
  • 思考一个业务场景:

    • 请编写一个功能,记录该网站实时的在线用户的个数。
    • 我们可以通过服务器端有没有分配session对象,因为一个session代表了一个用户。有一个session就代表有一个用户。如果你采用这种逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener够用了。session对象只要新建,则count++,然后将count存储到ServletContext域当中,在页面展示在线人数即可。
    • 业务发生改变了,只统计登录的用户的在线数量,这个该怎么办?
      • session.setAttribute("user", userObj);
      • 用户登录的标志是什么?session中曾经存储过User类型的对象。那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器,只要User类型对象存储到session域中,则count++,然后将count++存储到ServletContext对象中。页面展示在线人数即可。

改造oa项目:显示当前在线的人数:

  • 什么代表着用户登录了?
    • session.setAttribute("user", userObj); User类型的对象只要往session中存储过,表示有新用户登录。
  • 什么代表着用户退出了?
    • session.removeAttribute("user"); User类型的对象从session域中移除了。
    • 或者有可能是session销毁了。(session超时)

第一步:添加一个User类实现HttpSessionBindingListener接口:

package com.zwm.oa.bean;

import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;

import java.util.Objects;

/**
 * @author 猪无名
 * @date 2023/8/9 15 17
 * discription:
 */
public class User implements HttpSessionBindingListener {
    private String username;
    private String password;


    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        //用户登录了(User类型的对象向session中存放了。)
        ServletContext application = event.getSession().getServletContext();

        Object onlinecount = application.getAttribute("onlinecount");
        if(onlinecount == null){
            application.setAttribute("onlinecount",1);
        }else {
            int count = (Integer)onlinecount;
            count++;
            application.setAttribute("onlinecount",count);
        }
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        //用户退出了(User类型的对象从session中删除了。)
        ServletContext application = event.getSession().getServletContext();
        Integer onlinecount = (Integer)application.getAttribute("onlinecount");
        onlinecount--;
        application.setAttribute("onlinecount",onlinecount);

    }

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(username, user.username) && Objects.equals(password, user.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(username, password);
    }
}

第二步:更改原来的代码

在原来的登录和退出相关的代码处进行修改,主要有三处:

1:UserServlet类中的doExit方法,在消除session对象时,消除添加到session域中的user对象,实现了HttpSessionBindingListener接口的user对象会在被清除的时候,调用valueUnbound方法,从而实现application域中的数值减一。

 HttpSession session = request.getSession();
        if(session !=null){
            //从session域中删除user对象
            session.removeAttribute("user");

            //手动销毁session
            session.invalidate();
        }

2:UserServlet类中的doLogin方法,当登录验证成功之后,向session域中添加user对象。在添加的时候,user对象会调用valueBound方法,实现application域中的数值加一。

User user = new User(username,password);
session.setAttribute("user",user);

3:项目设置了默认的访问首页/welcome,它是用来实现cookie十天免登录的。假如在本地存储的有cookie对象,在判断信息无误后,也要跳转到项目首页,这时候也是需要增加在线人数的。

User user = new User(username,password);
session.setAttribute("user",user);

第三步:更改jsp代码,显示在线人数

<h2>欢迎${username},在线人数${onlinecount}</h2>
<%--这里的username也可以通过${user.username}获取。--%>



到此为止,老杜JavaWeb部分结束。