什么是监听器:
- 监听器是Servlet规范中的一员,就像Filter一样,Filter也是Servlet规范中的一员。
- 在Servlet中,所有监听器接口都是以“Listener”结尾。
监听器有什么用:
- 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。
- 特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。
Servlet规范中提供了哪些监听器:
jakarta.servlet包下:
- ServletContextListener
- ServletContextAttributeListener
- ServletRequestListener
- ServletRequestAttributeListener
jakarta.servlet.http包下:
- HttpSessionListener
- HttpSessionAttributeListener
- HttpSessionBindingListener
- HttpSessionActivationListener
实现一个监听器的步骤:以ServletContextListener为例:
第一步:编写一个类实现ServletContextListener接口,并实现里面的方法
void contextInitialized(ServletContextEvent sce)
void contextDestroyed(ServletContextEvent sce)
第二步:在web.xml文件中对ServletContextListener进行配置,如下:
<listener>
<listener-class>com.itzw.javaweb.listener.MyServletContextListener</listener-class>
</listener>
当然也可以使用注解:@WebListener
注意:所有监听器都是不需要javaweb程序员调用的,由服务器负责调用,什么时候被调用呢?当某个特殊的事件发生之后,被web服务器调用。
经过测试,我们发现,监听器和域的声明周期一样,其中初始方法和销毁方法的执行时刻和他们对应的域的执行时刻是一样的,比如context监听器在服务器启动时执行,在服务器关闭时销毁;再比如request对应的监听器在每次请求时就执行一次初始和销毁。
package com.itzw.javaweb.listener;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
@WebListener
public class MyHttpSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("HttpSessionListener被创建了");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("HttpSessionListener被销毁了");
}
}
AttributeListener
在执行相关的代码的时候它们的方法就会执行,比如HttpSessionAttributeListener
package com.itzw.javaweb.listener;
import jakarta.servlet.annotation.WebListener;
import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingEvent;
@WebListener
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
System.out.println("session添加值了");
}
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
System.out.println("session被删除了了");
}
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
System.out.println("session被代替了");
}
}
如上:在session被添加信息、代替、删除的时候会调用这几个方法。其它也是同理。
BindingListener
- 该监听器不需要使用@WebListener进行标注
- 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件;假设Customer没有实现该监听器,那么Customer对象放入或者从session中删除的时候,不会触发bind和unbind事件。
- 注意和AttributeListener区分开:AttributeListener需要@WebListener,它是只要有内容放入session或者别的操作都会触发AttributeListener。大致可以理解为,一个局部一个全局,一个范围小一个范围大
思考一个业务场景:
- 编写一个功能,记录该网站实时的在线用户的个数。
- 我们可以通过服务器有没有分配session对象,因为一个session代表了一个用户,这种情况HttpSessionListener就够用了。
业务发生变化,只统计登录的用户的在线人数:
- session.setAttribute("user",userObj);
- 用户登录的标志是什么?session中曾经存储过User类型的对象,那么这个时候可以让User类型的对象实现HttpSessionBindingListener监听器,只要User类型对象存储到session域中,则cout++,然后count++存储到ServletContext中,页面展示在线人数即可。
改造oa项目:添加在线人数功能:
根据上面的要求,创建一个User类:
package com.itzw.oa.bean;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionBindingEvent;
import jakarta.servlet.http.HttpSessionBindingListener;
public class User implements HttpSessionBindingListener {
@Override
public void valueBound(HttpSessionBindingEvent event) {
//获取ServletContext对象
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);
}
private String username;
private String password;
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 String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
接下来,要对之前的代码进行一丢丢的修改,把用户名和密码装进这新创建的User类即可。