Servlet事件监听器

Servlet事件监听器

Servlet事件监听器可以监听ServletContext、HttpSession、ServletRequest等域对象的创建和销毁过程,以及监听这些域对象属性的修改。

Servlet事件监听器概述

在监听的过程中会涉及几个重要组成部分:
1)事件:用户的一个操作,如单击一个按钮、调用一个方法、创建一个对象。
2)事件源:产生事件的对象。
3)事件监听器:负责监听发生在事件源上的事件。
4)事件处理器:监听器的成员方法,当事件发生的时候会触发对应的处理器
事件监听器在进行工作时,可以分为几步:
1)将监听器绑定到事件源,也就是注册监听器。
2)事件发生时会触发监听器的成员方法,即事件处理器,传递事件对象。
3)事件处理器通过事件对象获得事件源,并对事件源进行处理。
根据监听事件不同可以将其分为三类
1)用于监听域对象创建和销毁的事件监听器(ServletContextListener接口、HttpSessionListener接口、ServletRequestListener接口)。
2)用于监听域对象属性增加和删除的事件监听器(ServletContextAttributeListener接口、HttpSessionBindingListener接口、ServletRequestAttributeListener接口)。
3)用于监听绑定到HttpSession域中某个对象状态的事件监听器(HttpSessionBindingListener接口、HttpSessionActivationListener接口)。
在Servlet规范中,这三类事件监听器都定义了相应的接口,在编写事件监听器程序时只需实现对应的接口就可以。Web服务器会根据监听器所实现的接口,把它注册到被监听的对象上,当触发了某个对象的监听事件时,Web容器会调用Servlet监听器与之相关的方法对事件进行处理。

ServletContextListener接口

ServletContextListener对象是Web应用程序中一个非常重要的对象,为了监听该对象的创建与销毁过程,Servlet API中提供了一个ServletContextListener接口,当在Web应用程序中注册一个或多个实现了ServletContextListener接口的事件监听器时,Web容器在创建或销毁每个ServletContext对象时就会产生一个与其对应的事件对象,然后依次调用每个ServletContext事件监听器中的处理方法,并将ServletContext事件传递给这些方法,来完成事件的处理工作。
1、contextInitialized()方法

public void contextInitialized(servletContextEvent sce)

2、contextDestroyed()方法

public void contextDestroyed(servletContextEvent sce)

HttpSessionListener接口

HttpSessionListener用于完成会话操作,为了监听HttpSession对象的创建和销毁过程,Servlet API中提供了一个HttpSessionListener接口,当Web应用程序中注册一个或多个实现了HttpSessionListener接口的事件监听器时,Web容器在创建或销毁每个HttpSession对象时就会产生一个HttpSessionEvent事件对象,然后依次调用每个HttpSession事件监听器中的相应方法,并将HttpSessionEvent事件对象传递给这些方法。
1、sessionCreated()方法

public void sessionCreated(HttpSessionEvent se)

2、sessionDestroyed()方法

public void sessionDestroyed(HttpSessionEvent se)

ServletRequestListener接口

ServletRequest对象用于获取客户端发送的请求数据,为了监听ServletRequest对象的创建过程和销毁过程,Servlet API提供了ServletRequestListener接口,当Web应用程序中注册了一个或多个实现了ServletRequestListener接口的事件监听器时,Web容器在创建或销毁每个ServletRequest对象时都会生成一个ServletRequestEvent事件对象,然后依次调用每个ServletRequest事件监听器的方法。
1、requestInitialized()方法

public void requestInitialized(ServletRequestEvent sre)

2、requestDestroyed()方法

public void requestDestroyed(ServletRequestEvent sre)

案例_监听域对象的声明周期

要想对Servlet域对象的生命周期进行监听,首先需要实现域对应得接口,这些接口中的方法和执行过程非常类似。可以为每一个监听器编写一个单独的类,也可以用一个类实现这三个接口,从而让这个类具有三个事件监听器的功能。
1)创建一个listener包,编写MyListener.java

package listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MyListener implements ServletRequestListener, HttpSessionListener, ServletContextListener {
    public void contextInitialized(ServletContextEvent arg0)  { 
    	System.out.println("ServletContext对象被创建了");
    }
    public void contextDestroyed(ServletContextEvent arg0)  { 
    	System.out.println("ServletContext对象被销毁了");
    }
    public void sessionCreated(HttpSessionEvent arg0)  { 
    	System.out.println("HttpSession对象被创建了");
    }
    public void sessionDestroyed(HttpSessionEvent arg0)  { 
    	System.out.println("HttpSession对象被销毁了");
    }    
    public void requestInitialized(ServletRequestEvent arg0)  { 
    	System.out.println("ServletRequest对象被创建了");
    }
    public void requestDestroyed(ServletRequestEvent arg0)  { 
    	System.out.println("ServletRequest对象被销毁了");
    }
}

2)在web.xml中部署MyListener事件监听器
web.xml

<listener>
  	<listener-class>listener.MyListener</listener-class>
</listener>

需要注意的是,对于Servlet2.3规范,web.xml文件中的<listener>元素必须位于所有的<servlet>元素之前以及所有<filter-mapping>元素之后,否则Web容器在启动后会提示错误信息,对于Servlet2.4及以后的规范,这些同级元素之间的顺序可以任意。
一个完整的Servlet事件监听器包括Listener类和<listener>配置。一个web.xml中可以配置多个监听器。同一种类型的监听器也可以配置多个,触发的时候服务器会顺序执行各个监听器的相应方法。
注解方法
listener也可以通过注解来通知tomcat部署。

@WebListener

3)部署工程
在这里插入图片描述
从控制台可以看到,Web应用加载时,创建了ServletContext对象,所以监听器输出了对象被创建的信息。
4)停止工程
在这里插入图片描述
5)为了看到其他监听器运行效果,编写一个简单的jsp
myjsp.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
这是一个测试监听器的页面
</body>
</html>

在这里插入图片描述
tomcat默认session超时时间是30分钟,我们可以通过web.xml来修改session超时时间。

  <session-config>
  	<session-timeout>1</session-timeout>
  </session-config>

在这里插入图片描述

监听域对象中的属性变更

ServletContext、HttpSession、ServletRequest对象,都可以创建、删除和修改他们各自的属性,为了监听这三个对象的属性变更,Servlet API专门提供了一些接口,ServletContextAttributeListener,HttpSessionAttributeListener和ServletRequestAttributeListener接口,分别用于监听ServletContext对象中的属性变更,监听HttpSession对象中的属性变更,监听ServletRequest对象中的属性变更。
1.attributeAdded()方法
当向被监听的域对象中增加一个属性时,Web容器就调用事件监听器的attributeAdded()方法进行响应,该方法接收一个事件类型的参数,监听器可以通过这个参数来获取正在增加属性的域对象和被保存到域中的属性对象

public void attributeAdded(ServletContextAttributeEvent scab)
public void attributeAdded(HttpSessionBindindEvent scab)
public void attributeAdded(ServletRequestAttributeEvent scab)

2、attributeRemoved()方法
当删除被监听对象中的一个属性时,Web容器调用事件监听器的attributeRemoved()方法进行响应。这个方法在各个域属性监听器中的完整语法定义

public void attributeRemoved(ServletContextAttributeEvent scab)
public void attributeRemoved(HttpSessionBindindEvent scab)
public void attributeRemoved(ServletRequestAttributeEvent scab)

3、attributeReplaced()方法
当被监听器的域对象中的某个属性被替换时,Web容器会调用事件监听器的attributeReplaced()方法进行响应

public void attributeReplaced(ServletContextAttributeEvent scab)
public void attributeReplaced(HttpSessionBindindEvent scab)
public void attributeReplaced(ServletRequestAttributeEvent scab)

案例_监听域对象的属性变更

1)编写一个testattribute.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>这是一个测试对象属性信息监听器的页面</h3>
	<%
		getServletContext().setAttribute("username", "user1");
		getServletContext().setAttribute("username", "user2");
		getServletContext().removeAttribute("username");
		session.setAttribute("username", "user1");
		session.setAttribute("username", "user2");
		session.removeAttribute("username");
		request.setAttribute("username", "user1");
		request.setAttribute("username", "user2");
		request.removeAttribute("username");
	%>
</body>
</html>

注:这里直接使用getServletContext()可以成功调用ServletContext对象,这是因为java可以省略this.也就是说这里隐含的代码应该是this.getServletContext(),但是之所以能成功调用是因为jsp翻译的jspServlet继承了GenericServlet,而GenericServlet中定义了getServletContext方法
2)MyAttributeListener.java

package listener;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

@WebListener
public class MyAttributeListener implements ServletContextAttributeListener, HttpSessionAttributeListener, ServletRequestAttributeListener {

    public void attributeAdded(ServletContextAttributeEvent arg0)  { 
    	System.out.println("ServletContext添加属性:"+arg0.getName()+"="+arg0.getServletContext().getAttribute(arg0.getName()));
    }

    public void attributeRemoved(ServletContextAttributeEvent arg0)  { 
    	System.out.println("ServletContext移除属性:"+arg0.getName());
    }

    public void attributeRemoved(ServletRequestAttributeEvent arg0)  { 
    	System.out.println("ServletRequest移除属性:"+arg0.getName());
    }

    public void attributeAdded(ServletRequestAttributeEvent arg0)  { 
    	System.out.println("ServletRequest添加属性:"+arg0.getName()+"="+arg0.getServletRequest().getAttribute(arg0.getName()));
    }

    public void attributeReplaced(ServletRequestAttributeEvent arg0)  { 
    	System.out.println("ServletRequest替换属性:"+arg0.getName()+"="+arg0.getServletRequest().getAttribute(arg0.getName()));
    }

    public void attributeAdded(HttpSessionBindingEvent arg0)  { 
    	System.out.println("Session添加属性:"+arg0.getName()+"="+arg0.getSession().getAttribute(arg0.getName()));
    }

    public void attributeRemoved(HttpSessionBindingEvent arg0)  { 
    	System.out.println("Session移除属性:"+arg0.getName());
    }

    public void attributeReplaced(HttpSessionBindingEvent arg0)  { 
    	System.out.println("Session替换属性:"+arg0.getName()+"="+arg0.getSession().getAttribute(arg0.getName()));
    }

    public void attributeReplaced(ServletContextAttributeEvent arg0)  { 
    	System.out.println("ServletContext替换属性:"+arg0.getName()+"="+arg0.getServletContext().getAttribute(arg0.getName()));
    }
	
}

在这里插入图片描述

感知被HttpSession绑定的事件监听器

程序开发中经常使用Session域来存储对象,每个对象在该域中都有多种状态,如绑定(保存)到Session中,从Session域中解除绑定,随Session对象持久化到一个储存设备中(钝化),随Session对象从一个存储设备中恢复(活化)。为了观察Session域中对象的状态,Servlet API还提供了两个特殊的监听器接口HttpSessionBindingListener和HttpSessionActivationListener,这两个接口专门用于监听JavaBean对象在Session域中的状态

HttpSessionBindingListener接口

在使用JavaBean对象时经常会判断该对象是否绑定到Session域中,为此,Servlet API中专门提供了HttpSessionBindingListener接口,该接口用于监听JavaBean对象绑定到HttpSession和从HttpSession解绑的事件。
1、valueBound()方法

public void valueBound(HttpSessionBindingEvent event)

当对象被绑定到HttpSession对象中,Web容器将调用对象的valueBound()方法并传递一个HttpSessionBindingEvent类型的事件对象,程序可以通过这个事件对象来获得将要绑定到的HttpSession对象。
2、valueUnbound()方法

public void valueUnbound(HttpSessionBindingEvent event)

当对象从HttpSession解绑时,Web容器同样将调用对象的valueUnbound()方法并传递一个HttpSessionBindingEvent类型的事件对象
MyBean.java

package listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

@WebListener
public class MyBean implements HttpSessionBindingListener {

    public void valueBound(HttpSessionBindingEvent arg0)  { 
         System.out.println("MyBean对象被添加到了Session域..."+this);
    }

    public void valueUnbound(HttpSessionBindingEvent arg0)  { 
        System.out.println("MyBean对象从Session域移除了..."+this);
    }
}

testbinding.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="listener.MyBean"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
	session.setAttribute("myBean", new MyBean());
%>
</body>
</html>

在这里插入图片描述
当第一次访问jsp时,MyBean被添加到Session中,刷新浏览器,此时会显示另一个添加到了Session域中,第一个对象被移除了。

HttpSessionActivationListener接口

当一个会话开始时,Servlet容器会为会话创建一个HttpSession对象。Servlet容器在某些特殊情况下会把这些HttpSession对象从内存中转移至硬盘,这个过程称为持久化(钝化)。在持久化会话时,Servlet容器不仅会持久化HttpSession对象,还会对他所有可以序列化的属性进行持久化,从而确保存放在会话范围内的共享数据不会丢失。所谓可以序列化的属性就是指该属性所在的类实现了Serializable接口。当会话从持久化的状态变为运行状态的过程被称为活化,一般情况下,当服务器重新启动或者单个Web应用启动时,处于会话中的客户端向Web应用发出Http请求时,相应的会话会被激活。
为了监听HttpSession中对象活化和钝化的过程,Servlet API专门提供了HttpSessionActivationListener接口,该接口定义了两个事件处理方法,分别是sessionWillPassivate()方法和sessionDidActivate()方法
1、sessionWillPassivate()

public void sessionWillPassivate(HttpSessionEvent event)

当绑定到HttpSession对象中的对象将要随HttpSession对象被钝化之前,Web容器将调用这个方法并传递一个HttpSessionEvent类型的事件对象,程序通过这个事件对象可以获得当前被钝化的HttpSession对象。
2、sessionDidActivate()

public void sessionDidActivate(HttpSessionEvent event)

当绑定到HttpSession对象中的对象将要随HttpSession对象被活化之后,Web容器将调用这个方法并传递一个HttpSessionEvent类型的事件对象。
1)在完成会话的持久化时,会用到会话管理器PersistentManager,他的作用是当Web服务器终止或单个Web应用被终止时,会对被终止的Web应用的HttpSession对象进行持续化,会话管理器是在context.xml文件中的<Context>元素中配置的。在<Context>元素中增加一个<Manager>子元素便可以完成Session对象的持久化管理。

<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
	<Store className="org.apache.catalina.session.FileStore" directory="root"/>
</Manager>

1、<Manager>元素专门用于配置会话管理器,它的className属性用于指定负责创建、销毁和持久化Session对象的类。
2、maxIdleSwap属性用于指定session被钝化前的空闲时间间隔,本程序中将时间设置为1s,如果超过这个时间,管理Session对象的类将把Session对象持久化到存储设备中。
3、<Store>元素用于负责完成具体的持久化任务的类
4、directory属性指定保存持久化文件的目录,如果采用相对目录,它是相对于Web应用的工作目录而言的,也就是<Tomcat安装目录>/work/Catalina/localhost/chapter05/itcast。
2)编写一个MyBean2.java类,该类实现了HttpSessionActivationListener接口,并实现这个接口中的所有方法

package listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;

public class MyBean2 implements HttpSessionActivationListener {
	
	private String name;
	private int age;
	
	
    public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public void sessionDidActivate(HttpSessionEvent arg0)  { 
         System.out.println("MyBean2的对象活化了...");
    }

    public void sessionWillPassivate(HttpSessionEvent arg0)  { 
    	System.out.println("MyBean2的对象钝化了...");
    }
	
}

3)编写一个write.jsp页面,这个页面中将MyBean2对象保存到Session对象中,以查看MyBean2对象感知自己的Session绑定事件。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="listener.MyBean2"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Session域中存入数据</h1>
<%
		MyBean2 myBean=new MyBean2();
		myBean.setName("Tom");
		myBean.setAge(20);
		session.setAttribute("myBean", myBean);
%>
</body>
</html>

4)编写一个read.jsp页面,在这个页面中读取session域中的对象

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="listener.MyBean2"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Session域中读取数据</h1>
姓名:${sessionScope.myBean.name}
年龄:${sessionScope.myBean.age}
</body>
</html>

访问write.jsp页面
在这里插入图片描述
访问read.jsp页面
在这里插入图片描述
稍后,命令行输出在这里插入图片描述
再次访问read.jsp
在这里插入图片描述
发现此时不能取到用户信息了
这是因为当Servlet容器持久化一个HttpSession对象时,不会持续化存放在其中的MyBean2对象,当HttpSession对象被重新加载到内存后,它的MyBean2对象信息丢失了,因此没有用户信息,原本监听MyBean2对象活化的语句也没有输出。(此时HttpSession确实活化了,但是MyBean2丢失了)
为什么会丢失呢?
持久化HTTP Session地本质就是将HttpSession对象序列化存放在磁盘中,在下次访问时从磁盘中读取并反序列化,但MyBean2对象没有被JVM序列化,这是因为它没有实现Serializable接口(实现Serializable接口地类才会被JVM序列化)。

我们令MyBean2实现Serializable接口,就可以在HttpSession对象持久化时一同持久化。

package listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import java.io.Serializable;

public class MyBean2 implements HttpSessionActivationListener,Serializable {
	private String name;
	private int age;
    public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public void sessionDidActivate(HttpSessionEvent arg0)  { 
         System.out.println("MyBean2的对象活化了...");
    }
    public void sessionWillPassivate(HttpSessionEvent arg0)  { 
    	System.out.println("MyBean2的对象钝化了...");
    }
}

在访问write.jsp和read.jsp
在这里插入图片描述
在这里插入图片描述
在钝化后再次访问,发现输出活化语句
在这里插入图片描述
数据没有丢失
在这里插入图片描述

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值