前言
学习JavaWeb, 肯定要学Javaweb3个组件:Servlet、Listener、Filter
前面了解了Servlet,接下来是Listener
目录
- Listener简介
- 监听器
- Listener原理
- 监听ServletContext域对象的创建和销毁
- 其他Listener
- 验证监听器
6.1. HttpSessionBindingListener
6.2. HttpSessionActivationListener - 总结
Listener简介
Listener监听器大致是这样的:
Listener监听器有8种,它针对Web的三个域对象:HttpSession、ServletContext、ServletRequset,各有一个生命周期监听器和属性监听器
还有两个感知监听HttpSessionBindingListener(绑定与解绑)、HttpSessionActivationListener(钝化与活化)
监听器
监听器:实现了特定接口的一个Java程序,用于监听另一个Java程序的方法调用或者属性、状态改变,当监听到相应的事件时触发某种事件
有个问题,监听器是怎么监听的?
监听器怎么知道一个类发生了改变或者被调用??
只能让被监听的类告诉监听器,它被调用了或者是它被改变了
我们可以通过自己设计一个简单的监听器了解监听器的概念
一个PersonListener监听Person的动作:
监听器可以分为3个角色:监听器,被监听者,事件对象
当被监听者发生动作时,它会告诉监听者“我要动了”,并把事件对象(this)给监听者
被监听者Person.java
package com.company.Listen;
public class Person {
private PersonListen personListen;
private String name;
public Person(PersonListen personListen,String name) {
this.personListen = personListen;
this.name = name;
}
public String getName() {
return name;
}
public void eat(){
//注册监听器,并传递事件对象
personListen.doEat(new Event(this));
}
public void run(){
//注册监听器,并传递事件对象
personListen.doRun(new Event(this));
}
}
监听器PersonListener.java
package com.company.Listen;
public class PersonListen {
public void doEat(Event event){
System.out.println("监视到"+event.getSource().getName()+"正在eat");
}
public void doRun(Event event){
System.out.println("监视到"+event.getSource().getName()+"正在run");
}
}
事件对象Event:提供被监听者的信息
package com.company.Listen;
public class Event {
private Person source;
public Event(Person source) {
this.source = source;
}
public Person getSource() {
return source;
}
public void setSource(Person source) {
this.source = source;
}
}
PersonTest:
package com.company.Listen;
public class PersonTest {
public static void main(String[] args) {
Person person = new Person(new PersonListen(),"zhangsan");
person.eat();
person.run();
}
}
可以看到,当person开始eat、run时,监听器监听到了动作,并触发监听器的方法
这是监听器模式的思想,也是JavaWeb中Listener的思想
Listener原理
前面了解了监听器模式,在去看Javaweb中的监听器,会发现清晰了许多
以ServletContextListener为例(3个域对象的Listener都差不多)
ServletContextListener是用来监听ServletContext的生命周期
他提供了两个空方法供我们自定义监听事件,并传入了ServletContextEvent对象
ServletContextEvent是什么呢?
他就是事件对象,封装了被监听者ServletContext
到这里就对Listener原理很清晰了,当然可以继续深入到ServletContext
但因为ServletContext是由服务器实现的,暂时没有这能力
Listener原理:被监听者会注册监听器,当有事件发生时,将事件对象封装传递给监听器,触发监听器对应的事件(程序员自定义)
监听ServletContext域对象的创建和销毁
实现ServletContextListener 接口,重写contextInitialized、contextDestroyed放过
package listen;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println(sce.getServletContext().getClass()+"被创建了。。。");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println(sce.getServletContext().getClass()+"被销毁了。。。");
}
}
在web.xml上配置Listener
<listener>
<listener-class>listen.MyServletContextListener</listener-class>
</listener>
来验证一下
运行Tomcat
当关闭Tomcat服务器时:
这个顺便验证了ServletContext的生命周期:服务器运行就会创建ServletContext,服务器关闭销毁ServletContext
ServletContext是由服务器创建:org.apache.catalina.core.ApplicationContextFacade
其他Listener
还是这张图,这就是JavaWeb内置的监听器
- ServletContextListener、HttpSessionListener、ServletRequestListener都类似,都是监听该元素的生命周期:创建与销毁
- ServletContextAttributeListener、HttpSessionAttributeListener、ServletRequestAttributeListener也是类似的
不监听生命周期,改成监听ServletContext对象中属性的增加、删除、替换
- HttpSessionBindingListener是绑定与解绑,针对Session
我们前面学习Session时对Session的使用有了解
真的了解Session吗 - Session详解
Session类似与Map
绑定:setAttribute(String var1, Object var2),将元素保存(绑定)在Session
解绑:removeAttribute(String var1),通过name将元素从Session中移除(解绑)
- HttpSessionActivationListener活化与钝化
也就是对Session序列化的感知
验证监听器
对元素的生命周期、属性变化的监听都类似
主要验证一下HttpSessionBindingListener和HttpSessionActivationListener
HttpSessionBindingListener
如果一个对象实现了HttpSessionBindingListener接口,当这个对象被绑定到Session中或者从session中被删除时,Servlet容器会通知这个对象,而这个对象在接收到通知后,可以做一些初始化或清除状态的操作
注意:是实现了该接口的对象绑定到Session
编写一个BindingListener类实现HttpSessionBindingListener 接口:
package listen;
import javax.servlet.http.*;
public class BindingListener implements HttpSessionBindingListener {
private String name;
public BindingListener(String name) {
this.name = name;
}
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println(event.getName()+"-"+name+"加入了Session");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println(event.getName()+"-"+name+"被移出了Session");
}
}
在SessionTest 中加入监听器对象
package Session;
import listen.BindingListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class SessionTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到Session
HttpSession httpSession = req.getSession();
//存入数据
httpSession.setAttribute("bindingListener",new BindingListener("zhangsan"));
}
}
记得将SessionTest配置web.xml,监听器BindingListener在这仅作为一个存储数据,不需要配置web.xml
我们访问http://localhost:8080/sessionTest
监听到了Session中属性的绑定
关闭服务器(因为Session钝化了,而BindingListener对象没有实现序列化接口,即会被销毁):
当然,Session中的属性也被销毁了
HttpSessionBindingListener监听器与其他监听器不同,因为它绑定的是一个Session中的元素,所以它仅可以监听实现这个接口的元素是否绑定Session
注意:HttpSessionBindingEvent 对象有一些方法:
getSession可以得到当前对象绑定的Session
getName获得当前对象绑定的Session的name
getValue获得Session的vlaue
构造方法是给服务器用的
HttpSessionActivationListener
我们在Session中学习到,在服务器正常关闭的情况下,如果Session还存活,Session会钝化
那我们来验证一下HttpSessionActivationListener:
HttpSessionActivationListener和HttpSessionBindingListener一样,也是需要一个类实现这个接口,然后这个类作为属性存入Session中才可以监控Session的钝化与活化
创建一个Activation类继承HttpSessionActivationListener
并且要继承Serializable 使Activation对象能随着Session一起钝化
package listen;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;
public class Activation implements HttpSessionActivationListener, Serializable {
String name;
public Activation(String name) {
this.name = name;
}
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println(se.getSession().getId()+" - "+name+"被钝化。。。");
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println(se.getSession().getId()+" - "+name+"被活化。。。");
}
}
在SessionTest里加上:
httpSession.setAttribute("ActuvationListener",new Activation("李四"));
我们知道如果服务器正常关闭,而Session保存的属性也是可以序列化的对象,对象会跟随Session一起序列化(钝化)
先刷新http://localhost:8080/sessionTest
再关闭服务器
显示已经被钝化了
再启动:
Session钝化文件不见了
但是由于我们重启后在运行这个界面,会创建新的Session,原来的Session也就找不到了
我们写一个SessionTest1找到这个Session
package Session;
import listen.Activation;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
public class SessionTest1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
Activation activation = (Activation) session.getAttribute("ActuvationListener");
String name = activation.getName();
System.out.println(name);
PrintWriter writer = resp.getWriter();
writer.write(name);
}
}
记得配置进web.xml
再演示一遍:
关闭服务器钝化:
重启服务器:
运行SessionTest1
依旧找不到???
问题出在,我们每次在IDEA中关闭Tomcat都会删除Tomcat_9_0_22_JavaWeb目录,每次启动都会创建该目录,而Session钝化在其下肯定找不到
怎么改呢?
去自己的Tomcat目录下conf的context.xml文件中
在< Context>节点内加上
<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
<Store className="org.apache.catalina.session.FileStore" directory="D:\conf\Session"/>
</Manager>
directory是钝化文件放置的位置,自定义
再次重试:
总结
- Listener是监听器,有三个角色:监听器、被监听器、事件对象
- 被监听者会注册监听器,当有事件发生,将事件源(被监听者对象)封装成事件对象传递给监听器,监听器由程序员复制编写处理事件
- JavaWeb中有6+2种监听器,分别是监听3个域对象:ServletContext、HttpSession、ServletRequest的生命周期监听器与属性变化监听器和两个Session的绑定解绑、钝化活化监听器
- 前6中都类似,都有两个空方法由程序员编写:创建监听与销毁监听
- HttpSessionBindingListener、HttpSessionActivationListener两个与前6中监听器不同,这两种不需要在web.xml中配置,他们需要创建类实现这两个接口,然后将实现类加入Session,监听实现类的绑定与解绑,监听Session的钝化与活化