老杜 JavaWeb 讲解(二十) ——Listener监听器
(十八)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恢复到内存。
- jakarta.servlet包下:
-
实现一个监听器的步骤:以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}获取。--%>