监听器模型
普遍的监听器概念:
监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。监听器其实就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法立即被执行。
而javaweb中的监听器概念为:
监听器是用于监听web应用中某些对象、信息的创建、销毁、增加,修改,删除等动作的发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法。常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等。
一个监听器模型有几个重要的对象:
- 事件源
- 事件对象
- 监听器对象
如下图是一个普遍的监听器模型:
基于普遍监听器模型的案例
1、事件源
事件源是需要被监听的对象,当需要被监听时注册监听器,在需要监听的行为或者变量发生或者进行改变时生成相应的事件对象传入监听器中进行处理。
/**
*
*/
package listen.general;
/**
* @author Administrator
*
*/
public class Person {
private PersonListener listen;
public void eat() {
if (listen != null) {
listen.doeat(new Event(this));
}
}
public void run() {
if (listen != null) {
listen.dorun(new Event(this));
}
}
public void registListener(PersonListener listen) {
this.listen = listen;
}
}
2、事件对象
/**
*
*/
package listen.general;
/**
* @author Administrator
*
*/
public class Event {
private Object source;
public Event() {
}
public Event(Object source) {
this.source = source;
}
public void setSource(Object source) {
this.source = source;
}
public Object getSource() {
return source;
}
}
3、监听器对象
监听器在定义时可以是接口,在注册监听器是生成匿名类对象
/**
*
*/
package listen.general;
/**
* @author Administrator
*
*/
public class PersonListener {
/**
*
*/
public void doeat(Event e) {
System.out.println(e+":人吃东西");
}
/**
*
*/
public void dorun(Event e) {
System.out.println(e+":人在奔跑");
}
}
4、测试类
/**
*
*/
package listen.general;
/**
* @author Administrator
*
*/
public class TestListen {
public static void main(String[] args) {
PersonListener listen = new PersonListener();
Person person = new Person();
person.registListener(listen);
person.eat();
person.run();
}
}
运行输出结果:
listen.general.Event@63e2203c:人吃东西
listen.general.Event@1efed156:人在奔跑
javaweb中的监听器
JavaWeb中的监听器是Servlet规范中定义的一种特殊类,它用于监听web应用程序中的ServletContext, HttpSession和 ServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。
监听器的分类:
对象的监听
1、按监听的对象划分,可以分为
- ServletContext对象监听器
- HttpSession对象监听器
- ServletRequest对象监听器
2、按监听的事件划分
- 对象自身的创建和销毁的监听器
- 对象中属性的创建和消除的监听器
- session中的某个对象的状态变化的监听器
监听域对象中属性变更
1、按监听的对象的属性划分,可以分为
- ServletContextAttributeListener
- HttpSessionAttributeListener
- ServletRequestAttributeListener
2、按属性变更划分
- 属性增加
- 属性删除
- 属性替换
3、类对象的特殊监听器
- HttpSessionBindingListener
- HttpSessionActivationListener
ServletContext,HttpSession,ServletRequest都是域对象,可以在其中设置属性
接下来我们研究研究监听器的应用,其中类似的监听器应用我就只举一个例子了(都差不多)
监听ServletRequest域对象的创建和销毁
ServletRequestListener接口用于监听ServletRequest 对象的创建和销毁
Request对象被创建时,监听器的requestInitialized(ServletRequestEvent sre)方法将会被调用
Request对象被销毁时,监听器的requestDestroyed(ServletRequestEvent sre)方法将会被调用
ServletRequest域对象创建和销毁时机:
- 创建:用户每一次访问都会创建request对象
- 销毁:当前访问结束,request对象就会销毁
示例代码
/**
*
*/
package listen.web;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
/**
* @author Administrator
*
*/
@WebListener
public class MyServletRequestListener implements ServletRequestListener{
@Override
public void requestDestroyed(ServletRequestEvent request) {
System.out.println("请求Resquest销毁");
}
@Override
public void requestInitialized(ServletRequestEvent response) {
System.out.println("请求Resquest创建");
}
}
@WebListener是监听器的注解配置方式
也可以在web.xml中如下配置
<listener>
<description>ServletContextListener监听器</description>
<listener-class>listen.web.MyServletRequestListens</listener-class>
</listener>
访问web的结果为
请求Resquest创建
编码过滤器放行前
编码过滤器放行后
请求Resquest销毁
可以看到监听器的执行在过滤器的前面,且一次访问结束后request对象就会销毁
ServletRequestAttributeListener监听器
1、jsp代码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>测试ServletRequestAttributeListener</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%
request.setAttribute("test", "bb");
//替换request域对象中aa属性的值
request.setAttribute("test", "xx");
//移除request域对象中aa属性
request.removeAttribute("test");
%>
</body>
</html>
2、监听器代码
/**
*
*/
package listen.web;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.annotation.WebListener;
/**
* @author Administrator
*
*/
@WebListener
public class MyRequestAndSessionAttributeListener implements ServletRequestAttributeListener {
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("Request域添加属性");
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("Request域删除属性");
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("Request域替换属性值");
}
}
3、运行结果
Request域添加属性
Request域替换属性值
Request域删除属性
感知Session绑定的事件监听器HttpSessionBindingListener
1、jsp代码
<%@page import="listen.bean.BindingListenerBean"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>HttpSessionBindingListener监听器</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%
//将javabean对象绑定到Session中
session.setAttribute("bean", new BindingListenerBean("影梧"));
//从Session中删除javabean对象
session.removeAttribute("bean");
%>
</body>
</html>
2、bean对象
/**
*
*/
package listen.bean;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Data 该注解能够自动创建出来get、set、toString、equals、hashCode等方法
* @NoArgsConstructor 这个是无参构造器
* @AllArgsConstructor 全参构造器
* @Setter set方法
* @Getter get方法
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BindingListenerBean implements HttpSessionBindingListener{
private String name;
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("对象被绑定到session域");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("对象从session域解绑");
}
}
运行结果
对象被绑定到session域
对象从session域解绑
HttpSessionActivationListener监听器
实现了HttpSessionActivationListener接口的JavaBean对象可以感知自己被活化(反序列化)和钝化(序列化)的事件
当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被钝化(序列化)之前,web服务器调用该javabean对象的void sessionWillPassivate(HttpSessionEvent event) 方法。这样javabean对象就可以知道自己将要和HttpSession对象一起被序列化(钝化)到硬盘中,以及被火化到虚拟机内存的动作。
当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被活化(反序列化)之后,web服务器调用该javabean对象的void sessionDidActive(HttpSessionEvent event)方法。这样javabean对象就可以知道自己将要和 HttpSession对象一起被反序列化(活化)回到内存
为了观察绑定到HttpSession对象中的javabean对象随HttpSession对象一起被钝化到硬盘上和从硬盘上重新活化回到内存中的的过程,我们需要借助tomcat服务器帮助我们完成HttpSession对象的钝化和活化过程,具体做法如下:
在WebRoot\META-INF文件夹下创建一个context.xml文件,如下所示:
<Context>
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
<Store className="org.apache.catalina.session.FileStore" directory="gacl"/>
</Manager>
</Context>
META-INF
相当于一个信息包,目录中的文件和目录获得Java平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务
代码
/**
*
*/
package listen.bean;
import java.io.Serializable;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Administrator
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@WebListener
public class SerializListener implements HttpSessionActivationListener, Serializable{
private String name;
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println(name+"和session一起被序列化(钝化)到硬盘了,session的id是:"+se.getSession().getId());
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println(name+"和session一起从硬盘反序列化(活化)回到内存了,session的id是:"+se.getSession().getId());
}
}
<%@page import="listen.bean.SerializListener"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>HttpSessionActivationListener监听器</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%
session.setAttribute("bean",new SerializListener("影梧"));
%>
</body>
</html>
运行测试
访问这个jsp页面,服务器就会马上创建一个HttpSession对象,然后将实现了HttpSessionActivationListener接口的JavaBean对象绑定到session对象中,这个jsp页面在等待1分钟之后没有人再次访问,那么服务器就会自动将这个HttpSession对象钝化(序列化)到硬盘上。
如图,访问页面等待1分钟后
程序运行结果: