WEB核心之监听器(Listener)

1 什么是监听器

  监听器就是监视web应用里面的某些对象的变化而进行一些处理操作。

  监听器的一些相关概念:

  • 事件源:就是被监视的对象。
  • 监听器对象:监视事件源的对象。
  • 注册(绑定):将监听器与事件源进行绑定,当事件源对象发生改变时就会触发监听器对象。
  • 事件:监听器监听到事件源发生改变时的响应行为。
  • 事件对象:在监听器对象里获得事件源对象。

2 监听器的分类

​  JavaEE规范中有8个监听器接口,用于监听不同的WEB对象。具体的分类如下表所示:

除了以上6个监听器,还有两个与Session作用域相关的监听器,他们分别是:
  • 绑定和解绑:HttpSessionBindingListener
  • 钝化和活化:HttpSessionActivationListener

3 监听域对象创建和销毁

使用方法

  • 创建一个对象实现对应的监听器的接口。
  • 重写监听器接口的抽象方法。
  • 配置(注册)监听器。(web.xml或注解)

3.1 ServletContextListener

​  此监听器是监听ServletContext对象的创建和销毁,使用方法是创建一个类实现该监听器接口,重写里面的抽象方法,还需在web.xml文件中配置监听器或者使用注解配置,否则不起作用。

public class MyServletContextListener implements ServletContextListener {
    
    //监听ServletContext对象的创建
    public void contextInitialized(ServletContextEvent sce)  { 
        System.out.println("ServletContext对象的创建啦");
    }
	
	//监听ServletContext对象的销毁
    public void contextDestroyed(ServletContextEvent sce)  { 
    	System.out.println("ServletContext对象的销毁了");
    }
}

配置文件使用web.xml配置,也可用注解配置。

<listener>
    <listener-class>com.gsgb.create.MyServletContextListener</listener-class>
</listener>
  • 当服务器启动时创建了ServletContext对象,此时监听器监听到了ServletContext对象的创建,就会调用contextInitialized方法,在控制台打印。
  • 当服务器正常关闭时,当监听器监听到了ServletContext对象销毁了,在销毁之前调用contextDestroyed方法。

SerVletContextEvent对象

​  从上面的代码中,我们可以看到两个方法里面的参数都是ServletContextEvent对象,它是一个事件对象,可以通过它获取被监听的对象,即事件源。具体方法如下:

ServletContext getServletContext();	//获取事件源

​  ServletContextEvent类继承EventObject类,EventObject类里面有个通用的方法也可以获得事件源对象,只不过返回值是Object类型的,具体如下:

Object getSource();//获取事件源

​  其实getServletContext()方法的内部也是调用它的父类的getSource()方法,方法源码如下:

public ServletContext getServletContext() {
	return (ServletContext) super.getSource();
}

ServletContextListener的作用

  • 通过监听ServletContext对象的创建,我们可以做一些初始化的工作,如初始化一些对象或数据,加载数据库驱动和连接池的初始化等。
  • 加载一些初始化的配置文件 ,如spring的配置文件。
  • 任务调度(定时器)——Timer/TimerTask。

了解了上面的内容,我们来写一个模拟银行计息的例子,要求服务器启动后每天晚上12点开始计息。

public class MyServletContextListener implements ServletContextListener {
    
    //监听ServletContext对象的创建
    public void contextInitialized(ServletContextEvent sce)  { 
    	
    	//获取服务器开启的当天晚上12点的日期
    	Calendar cal = Calendar.getInstance();
    	cal.add(Calendar.DAY_OF_YEAR, 1);
    	cal.set(Calendar.HOUR_OF_DAY, 0);
    	cal.set(Calendar.MINUTE, 0);
    	cal.set(Calendar.SECOND, 0);
    	cal.set(Calendar.MILLISECOND, 0);
        
    	Date date = cal.getTime();//此处已经获取到当天晚上12点的Date对象
    	
    	//创建Timer计时器对象
    	Timer timer = new Timer();
    	//通过Timer对象开启指定时间开始以固定的时间开始计息
    	timer.scheduleAtFixedRate(new TimerTask() {	
			@Override
			public void run() {
				//模拟银行计息
				System.out.println("银行计息一次...");
			}
		}, date, 24*60*60*1000);
    }
	
	//监听ServletContext对象的销毁
    public void contextDestroyed(ServletContextEvent sce)  { 
    	System.out.println("ServletContext对象的销毁了");
    }
}
<listener>
    <listener-class>com.gsgb.bank.MyServletContextListener</listener-class>
</listener>

3.2 HttpSessionListener

​​  HttpSessionListener的用法其实也和ServletContextListener差不多,只是他们监听的对象不同而已,此监听器是用来监听session对象的创建和销毁。

监听器代码:

public class MyHttpSessionListener implements HttpSessionListener {

	//监听session对象的创建
    public void sessionCreated(HttpSessionEvent se)  { 
    	System.out.println("session对象的创建了");
    }

	//监听session对象的销毁
    public void sessionDestroyed(HttpSessionEvent se)  { 
    	System.out.println("session对象的销毁了");
    }	
}

配置文件代码

 <listener>
    <listener-class>com.gsgb.create.MyHttpSessionListener</listener-class>
  </listener>

获取session对象代码

public class DemoServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		System.out.println("获取session前");
		
		//获取或创建一个session
		HttpSession session = request.getSession();
		
		System.out.println("获取session后");
		
		//延迟5s手动销毁session
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		session.invalidate();
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

​​  当我们访问获取session的资源时,在创建session的时候会触发监听器,当我们等待5s销毁的时候也会触发监听器。代码的运行结果如下图所示:

HttpSessionEvent对象

​ 该对象也是事件对象,可以通过它获取被它监听的对象:

  • 第一种方式:
HttpSession session = se.getSession();
  • 第二种方式:此对象和SerVletContextEvent对象一样也是EventObject的子类,故此可以用父类的方法,
HttpSession session = (HttpSession)se.getSource();

HttpSessionListener的作用

​​  在服务器开启后因为客户访问资源会产生多个session对象,因此每次客户访问的时候就会产生或获取一个session对象,每产生一个session对象就会触发监听器。基于这个原理我们可以再监听创建session的方法里计数来获得网站的访问量

3.3 ServletRequsetListener

​ 此监听器是用来监听request对象的创建和销毁。

public class MyServletRequestListener implements ServletRequestListener{

	@Override
	//监听request对象的创建
	public void requestInitialized(ServletRequestEvent sre) {
		ServletContext servletContext = sre.getServletContext();
		ServletRequest servletRequest = sre.getServletRequest();
		Object source = sre.getSource();
		System.out.println("request对象创建了");
	}
	
	@Override
	//监听request对象的销毁
	public void requestDestroyed(ServletRequestEvent sre) {
		System.out.println("request对象销毁了");
	}
}
 <listener>
    <listener-class>com.gsgb.create.MyServletRequestListener</listener-class>
  </listener>

ServletRequestEvent对象

​​  该事件对象也是EventObject类的子类,能够获取当前监听的request对象,还能获取ServletContext对象。

ServletContext servletContext = sre.getServletContext();
ServletRequest servletRequest = sre.getServletRequest();
Object source = sre.getSource();

4 监听域对象的属性变化

使用方法

  • 创建一个对象实现对应的监听器的接口。
  • 重写监听器接口的抽象方法。
  • 配置(注册)监听器。(web.xml或注解)

4.1 ServletContextAttibuteListener

public class MyServletContextAttibuteListener implements ServletContextAttributeListener{

	@Override
	//监听域内增加属性,获取增加的属性键值
	public void attributeAdded(ServletContextAttributeEvent scae) {
		System.out.println("域中的键值对:"+scae.getName()+" = "+scae.getValue());
		System.out.println("域中添加数据了");
	}

	@Override
	//监听域内删除属性,获取的是删除前的值
	public void attributeRemoved(ServletContextAttributeEvent scae) {
		System.out.println("域中的键值对:"+scae.getName()+" = "+scae.getValue());
		System.out.println("域中数据被删除了");
	}

	@Override
	//监听域内修改属性,获取的是修改前的值
	public void attributeReplaced(ServletContextAttributeEvent scae) {
		System.out.println("域中的键值对:"+scae.getName()+" = "+scae.getValue());
		System.out.println("域中数据修改了");
	}
}
<listener>
    <listener-class>com.gsgb.attr.MyServletContextAttibuteListener</listener-class>
  </listener>

添加数据的Servlet

public class TestServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		//获取ServletContext对象
		ServletContext context = this.getServletContext();
		//向域对象设置值
		context.setAttribute("game", "DOTA2");
		
		//向域对象修改值
		context.setAttribute("game", "LOL");
		
		//删除域对象的值
		context.removeAttribute("game");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

​​  当我们访问向域中添加数据的资源时,当域中数据增删改操作时就会触发监听器。所以上代码的结果是:

域中的键值对:game = DOTA2
域中添加数据了
域中的键值对:game = DOTA2
域中数据修改了
域中的键值对:game = LOL
域中数据被删除了

注意:监听器的attributeReplaced()方法里面通过事件对象获取的键和值是修改前的键和值

4.2 HttpSessionAttributeListener

​​  此监听器和ServletContextAttibuteListener监听器用法差不多。略。

4.3 ServletRequestAriibuteListenr

​​  此监听器和ServletContextAttibuteListener监听器用法差不多。略。

5 两个特殊的监听器

​​  两个特殊的监听器分别是HttpSessionBindingListener绑定解绑HttpSessionActivationListener钝化活化。这两个监听器的作用是监听即将要存储到session域中的对象的状态。有四种状态:

  • 绑定状态:就一个对象被放到session域中。
  • 解绑状态:就是这个对象从session域中移除了。
  • 钝化状态:是将session内存中的对象持久化(序列化)到磁盘。
  • 活化状态:就是将磁盘上的对象再次恢复到session内存中。

5.1 HttpSessionBindingListener

​此监听器是针对对象的绑定和解绑的。此监听器的使用方法如下:

  • 创建要监听的JavaBean对象,实现HttpSessionBindingListener接口。
  • 重写接口里的抽象方法。

注意:此监听器不需要配置。

JavaBean对象

public class User implements HttpSessionBindingListener{
	private String name;
	private int age;
	
	public User() {}
	
	public User(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

    //代码过长,我省略了get和set

	@Override
	//监听对象被绑定
	public void valueBound(HttpSessionBindingEvent event) {
		System.out.println("哎呀,我被绑了");
	}

	@Override
	//监听对象解绑
	public void valueUnbound(HttpSessionBindingEvent event) {
		System.out.println("哈哈,我解绑了");
	}	
}

测试代码

public class TestUserServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			
		//获取session对象
		HttpSession session = request.getSession();
		//创建JavaBean对象
		User user = new User("zhangsab", 23);
		//将JavaBean存到Session域中
		session.setAttribute("user", user);
		//将JavaBean从Session域中删除
		session.removeAttribute("user");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

执行结果

哎呀,我被绑了
user
哈哈,我解绑了
user

HttpSessionBindingEvent事件

​ HttpSessionBindingEvent类是HttpSessionEvent类的子类,他有如下的方法:

public String getName();//获取存储到session域的对象的名字,即键
public Object getValue() //获取存储到session域的对象,即值
public HttpSession getSession()//获取session对象(从父类继承的方法)

HttpSessionBindingListener和HttpSessionAttributeListener对比

  • HttpSessionAttributeListener需要在web.xml或注解注册,可以监听在session域中的任意对象。
  • HttpSessionBindingListener不需要注册,只能监听当前JavaBean对象。

5.2 HttpSessionActivationListener

​​  此监听器是监听存到session域中对象的钝化与活化的,钝化就是将session中的对象存到磁盘上,而活化就是将该对象恢复到内存中。

​​  我们为什么要钝化和活化呢,因为如果用户过多而且他长时间没有操作,我们还不能销毁session,防止删除了用户正在操作的内容,这样的话就会给服务器增加压力,所以钝化和活化的出现是十分有必要的,在用户没有操作的时候,我们就给他的session域里面的对象序列化到硬盘上,当用户再次操作网站的时候,就反序列化到内存中就可以了,这样就可以减小服务器的压力了。

使用此监听器的步骤如下:

  • 创建一个JavaBean,实现HttpSessionActivationListener接口和Serializable序列化接口。
  • 重写接口里的抽象方法。

注意:此监听器不需要配置

package com.itheima.domain;

import java.io.Serializable;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;

public class User implements HttpSessionActivationListener,Serializable{
	private String name;
	private int age;
	
	public User() {
		
	}
	
	//代码过长,我省略了get和set

	@Override
	//监听对象钝化
	public void sessionWillPassivate(HttpSessionEvent se) {
		System.out.println("我是User对象,我被钝化了");
	}

	@Override
	//监听对象活化
	public void sessionDidActivate(HttpSessionEvent se) {
		System.out.println("我是User对象,我被活化了");
		
	}	
}

注意钝化和活化的前提是服务器正常关闭和session存在,假如过期了等问题导致session没有了是不存在钝化和活化的。

​​  JavaBean我们已经写好了,那么存储在session域中的JavaBean对象何时钝化何时活化呢,假如不进行任何配置的话,默认是下面的情况:

  • 钝化:服务器正常关闭时就会钝化。
  • 活化:服务器启动后对象活化,我们就可与存取数据了。

​​  当对象被钝化时,会在tomcat路径\work\Catalina\localhost\项目名里面创建一个SESSION.ser文件。

​​  当对象被活化时,这个文件会消失,因为它又被反序列化到内存中去了。

​​  上面是默认的情况,那么我们该怎么利用配置文件来配置该对象何时钝化呢。我们需要写一个xml配置文件即可。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
 <!-- maxIdleSwap:session中的对象多长时间不使用就钝化 -->
 <!-- directory:序列化后文件存放路径 -->
 <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
  <Store className="org.apache.catalina.session.FileStore" directory="OutSession" />
 </Manager>
</Context>

​​  当我们使用了配置文件后,序列化后的文件就会输出到我们知道的文件夹里。

  如:Tomcat路径\work\Catalina\localhost\项目路径\OutSession

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值