详解JavaWeb中的 Listener

一、基本概念

JavaWeb里面的listener是通过观察者设计模式进行实现的。对于观察者模式,这里不做过多介绍,大概讲一下什么意思。

观察者模式又叫发布订阅模式或者监听器模式。在该模式中有两个角色:观察者和被观察者(通常也叫做主题)。观察者在主题里面注册自己感兴趣的事件,当这个事件发生时,主题会通过回调接口的方式通知观察者。

举个生活中的例子:订阅报纸。任何一个家庭或个人都可以向报社订阅报纸。这里报社就是“主题”,家庭就是“观察者”。比如家庭需要订阅明天早晨的报纸,这个就是“事件”。到了第二天早晨,报纸生产出来了,这个就是“事件发生”。当事件发生时,送报员将报纸送到家庭的信箱里面,这里的信箱就是“回调接口”。

对于JavaWeb里面的监听器,Servlet规范定义了一些列的Listener接口类,通过接口类的方式将事件暴露给应用程序,应用程序如果想监听其感兴趣的事件,那么不必去直接注册对应的事件,而是编写自己的listener实现相应的接口类,并将自己的listener注册到servlet容器。当程序关心的事件发生时,servlet容器会通知listener,回调listener里面的方法。这里自定义的listener就是观察者,servlet容器就是主题。

二、样例分析

上面说了,servlet容器是通过Listener接口类将事件暴露给应用程序的。所以我们与其说是注册事件,不如说是注册监听器。对应到编程步骤就是:1.编写自己的listener,实现特定的Listener接口。2.在web.xml里面注册自己的listener。这里以最简单的监听器接口ServletContextListener举例:

1.TestListener.java

public class TestListener implements ServletContextListener {
public TestListener() {}
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContextListener.contextInitialized");
}
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContextListener.contextDestroyed");
}
}

2.web.xml

<listener>
<listener-class>com.nantang.listener.TestListener</listener-class>
</listener>

当容器启动时会向日志中输出"ServletContextListener.contextInitialized",当容器关闭时会输出"ServletContextListener.contextDestroyed"。详细的解释后面会进一步分析。

这里需要注意是,如果在IDE(Eclipse、STS等)演示上面的例子,当启动服务器时,在控制台可以看到"ServletContextListener.contextInitialized",当关闭服务器时,是看不到"ServletContextListener.contextDestroyed"的。这不是没有执行contextDestroyed方法,而是IDE实现的不够完美。要想验证确实调用了contextDestroyed,可以在contextDestroyed里面写一段代码逻辑往文件输出内容而不要输出到控制台。

三、源码分析

现在我们分析下,servlet规范为我们定义了哪些事件。更准确的说是定义了哪些监听接口。下面的介绍都是以servlet3.0规范为准。

servlet3.0为我们提供了8个监听器接口,按照它们的作用域来划分的话可以分为三类:

1.servlet上下文相关监听接口,包括:ServletContextListener和ServletContextAttributeListener。

2.http session相关监听接口,包括:HttpSessionListener、HttpSessionActivationListener、HttpSessionAttributeListener和HttpSessionBindingListener。

3.servlet request相关监听接口,包括:ServletRequestListener和ServletRequestAttributeListener。

其实从接口的命名,各位看官应该能猜出其基本功能。下面我们按分类来解释。

1.servlet上下文相关监听接口

之前在介绍Servlet的时候,我们解释过一个web应用对应一个servlet上下文。所以ServletContextListener和ServletContextAttributeListener监听的事件的生命范围是贯穿整个web应用的。下面是这两个接口的类图层级关系。

1.1 EventListener

EventListener是一个标记接口,所有的事件监听器都必须继承这个接口,这就是servlet规范,没什么好解释的。

1.2 EventObject

和EventListener类似,EventObject是个事件顶级类,所有具体的事件类都必须继承EventObject。

public class EventObject implements java.io.Serializable {
protected transient Object source;
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
public Object getSource() {
return source;
}
public String toString() {
return getClass().getName() + "[source=" + source + "]";
}
}

这个类很简单,其本质就一个东西:source。通过类名EventObject和属性名source,就能看出这个类就干了一件事,持有“事件源对象”。

1.3 ServletContextEvent

public class ServletContextEvent extends java.util.EventObject {
public ServletContextEvent(ServletContext source) {
super(source);
}
public ServletContext getServletContext () {
return (ServletContext) super.getSource();
}
}

servlet上下文事件,这个事件类就是对EventObject的简单继承。构造方法中提供ServletContext实例作为事件源。因为事件源是servlet上下文,所以提供个getServletContext获取ServletContext实例。

在我们后续讲解其他事件类的时候,都是一个模子,每个事件类都提供相应的构造方法,传入相应的事件源对象,并提供额外的获取事件源方法。所以EventObject就是个事件源的基类,所有事件子类的本质就干了一件事,确定具体的事件源对象。

所以我们后面讲解事件的地方,一带而过。

1.4 ServletContextListener

public interface ServletContextListener extends EventListener {
public void contextInitialized ( ServletContextEvent sce );
public void contextDestroyed ( ServletContextEvent sce );
}

servlet上下文监听器接口,对应着两个事件:servlet上下文初始化事件和servlet上下文即将关闭事件。

当web应用初始化的时候,servlet容器会构造ServletContextEven实例,并回调contextInitialize方法。

当servlet上下文即将关闭时,一般是关闭服务器之前,servlet容器会构造ServletContextEven实例,并回调contextDestroyed方法。这里需要注意的是,contextDestroyed方法的执行会在所有的servlet和filter执行完destroy方法之后。

所以如果我们想在应用启动或关闭时需要做些事情的话,就编写自己的listener实现该接口。

所有的事件监听器也是一个模子,按照servlet规范定义相应的事件回调接口方法,方法的入参就是相应的事件源实例。所以我们后面讲解监听器的地方也一带而过。

1.5 ServletContextAttributeEvent

public class ServletContextAttributeEvent extends ServletContextEvent {
private String name;
private Object value;
public ServletContextAttributeEvent(ServletContext source, String name, Object value) {
super(source);
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public Object getValue() {
return this.value;
}
}

ServletContextAttributeEvent表示servlet上下文属性相关事件,一般当属性发生改变时会触发该事件。这个类继承ServletContextEven,事件源也是ServletContext实例。额外提供属性名和属性值的获取方法。

1.6 ServletContextAttributeListener

public interface ServletContextAttributeListener extends EventListener {
public void attributeAdded(ServletContextAttributeEvent scab);
public void attributeRemoved(ServletContextAttributeEvent scab);
public void attributeReplaced(ServletContextAttributeEvent scab);
}

当servlet上文属性发生增、删、改的时候,servlet容器构造ServletContextAttributeEvent事件对象,分别回调attributeAdded、attributeRemoved、attributeReplaced方法。

这里需要注意的是attributeReplaced方法,当属性的值被替换的时候回调。这个时候如果调用ServletContextAttributeEvent.getValue()方法返回的是替换之前的属性值。

2 http session相关监听接口

2.1 HttpSessionEvent

public class HttpSessionEvent extends java.util.EventObject {
public HttpSessionEvent(HttpSession source) {
super(source);
}
public HttpSession getSession () {
return (HttpSession) super.getSource();
}
}

http session相关事件,当session发生变化时会触发该事件。事件源是HttpSession实例,并提供额外的HttpSession获取方法。

2.2 HttpSessionListener

public interface HttpSessionListener extends EventListener {
public void sessionCreated ( HttpSessionEvent se );
public void sessionDestroyed ( HttpSessionEvent se );
}

当session被创建和销毁的时候,servlet容器构造HttpSessionEvent事件对象,并回调sessionCreated和sessionDestroyed方法。

2.3 HttpSessionActivationListener

public interface HttpSessionActivationListener extends EventListener {
public void sessionWillPassivate(HttpSessionEvent se);
public void sessionDidActivate(HttpSessionEvent se);
}

当session将要钝化或已被激活时,servlet容器构造HttpSessionEvent事件对象,回调sessionWillPassivate和sessionDidActivate方法。

这里解释下钝化和激活:钝化是指服务器内存不够了或者session的活动超时时间到了,把最近不活动的session序列化到磁盘。激活是指某个钝化的session又被访问了,从磁盘将session反序列化到内存。

这里可以看出要想钝化和激活,首先session得可序列化和反序列化。同时我们在编程过程中,session尽量用String、Integer等简单的对象,尽量不要用list、map等集合。

2.4 HttpSessionBindingEvent

public class HttpSessionBindingEvent extends HttpSessionEvent {
private String name;
private Object value;
public HttpSessionBindingEvent(HttpSession session, String name) {
super(session);
this.name = name;
}
public HttpSessionBindingEvent(HttpSession session, String name, Object value) {
super(session);
this.name = name;
this.value = value;
}
public HttpSession getSession () {
return super.getSession();
}
public String getName() {
return name;
}
public Object getValue() {
return this.value;
}
}

http session的属性相关事件,当session属性发生变化时会触发该事件。事件源是HttpSession实例,并提供额外的获取HttpSession、属性名、属性值的方法。

2.5 HttpSessionAttributeListener

public interface HttpSessionAttributeListener extends EventListener {
public void attributeAdded ( HttpSessionBindingEvent se );
public void attributeRemoved ( HttpSessionBindingEvent se );
public void attributeReplaced ( HttpSessionBindingEvent se );
}

当session属性发生增、删、改的时候,servlet容器构造HttpSessionBindingEvent事件对象,分别回调attributeAdded、attributeRemoved、attributeReplaced方法。

这里需要注意的是attributeReplaced方法,当属性的值被替换的时候回调。这个时候如果调用ServletContextAttributeEvent.getValue()方法返回的是替换之前的属性值。

当调用session的invalidate方法或者session失效时,也会回调attributeRemoved方法。

2.6 HttpSessionBindingListener

public interface HttpSessionBindingListener extends EventListener {
public void valueBound(HttpSessionBindingEvent event);
public void valueUnbound(HttpSessionBindingEvent event);
}

这个监听器也是监听session的属性变化。当session属性发生增和删,也就是属性值绑定和属性值解绑的时候,servlet容器构造HttpSessionBindingEvent事件对象,分别回调valueBound、valueUnbound方法。

这么一看和HttpSessionAttributeListener没什么区别,其实不是这样。两者有个本质的区别就是事件触发的条件。

当session的属性有任何的变化,servlet容器都会通知HttpSessionAttributeListener。但是对于HttpSessionBindingListener,只有当绑定或解绑的属性值是监听器的实例时,servlet容器才会通知。举例来说:

public class TestListener implements HttpSessionBindingListener{
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("HttpSessionBindingListener.valueBound");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("HttpSessionBindingListener.valueUnbound");
}
}

我们自定义监听器TestListener实现HttpSessionBindingListener,下面我们在代码中设置如下session属性:

HttpSession session = request.getSession();
TestListener testListener=new TestListener();
session.setAttribute("listener", testListener);
session.removeAttribute("listener");

这里session的属性值是我们的监听器TestListener实例。所以这段代码执行时,servlet容器会通知TestListener并回调valueBound和valueUnbound方法。

这里需要注意的是,当调用session的invalidate方法或者session失效时,也会回调valueUnbound方法。

3 servlet request相关监听接口

3.1 ServletRequestEvent

public class ServletRequestEvent extends java.util.EventObject {
private ServletRequest request;
public ServletRequestEvent(ServletContext sc, ServletRequest request) {
super(sc);
this.request = request;
}
public ServletRequest getServletRequest () {
return this.request;
}
public ServletContext getServletContext () {
return (ServletContext) super.getSource();
}
}

servlet请求的相关事件,当request发生变化时会触发该事件。事件源是ServletContext实例,并提供额外的获取ServletContext和ServletRequest方法。

3.2 ServletRequestListener

public interface ServletRequestListener extends EventListener {
public void requestDestroyed ( ServletRequestEvent sre );
public void requestInitialized ( ServletRequestEvent sre );
}

当请求初始化或者销毁时,即客户端请求进入web应用(进入servlet或者第一个filter)或web应用返回响应给客户端(退出servlet或者第一个filter)。servlet容器构造ServletRequestEvent实例,回调requestInitialized和requestDestroyed方法。

3.3 ServletRequestAttributeEvent

public class ServletRequestAttributeEvent extends ServletRequestEvent {
private String name;
private Object value;
public ServletRequestAttributeEvent(ServletContext sc, ServletRequest request, String name, Object value) {
super(sc, request);
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public Object getValue() {
return this.value;
}
}

servlet请求属性的相关事件,当请求属性发生变化时会触发该事件。事件源是ServletContext实例,并提供额外的获取属性名和属性值的方法。

3.4 ServletRequestAttributeListener

public interface ServletRequestAttributeListener extends EventListener {
public void attributeAdded(ServletRequestAttributeEvent srae);
public void attributeRemoved(ServletRequestAttributeEvent srae);
public void attributeReplaced(ServletRequestAttributeEvent srae);
}

当请求的属性发生增、删、改的时候,servlet容器构造ServletRequestAttributeEvent事件对象,分别回调attributeAdded、attributeRemoved、attributeReplaced方法。

这里需要注意的是attributeReplaced方法,当属性的值被替换的时候回调。这个时候如果调用ServletRequestAttributeEvent.getValue()方法返回的是替换之前的属性值。

四、总结

至此,listener讲完了。我们可以发现listener和servlet、filter有个共同点,都是由容器进行调度。我们只需要编写自己的listener去实现我们关心的监听器接口并注册,剩下来的工作就是在我们自己的listener里面编写业务逻辑。

这边博文介绍的listener是servlet3.0规范制定的。3.1已经增加了一些事件监听器接口,道理都是类似的,读者可以自行去了解。

(0)

相关推荐

  • java 在Jetty9中使用HttpSessionListener和Filter

    java 在Jetty9中使用HttpSessionListener和Filter HttpSessionListener 当Session创建或销毁的时候被调用 示例代码: class MyHttpSessionListener implements HttpSessionListener { @Override public void sessionCreated(HttpSessionEvent httpSessionEvent) { System.out.println("session

  • JavaWeb监听器Listener实例解析

    首先来介绍一下什么是监听器: 监听器-就是一个实现待定接口的普通Java程序,此程序专门用于监听另外一个类的方法调用. 这是使用观察者模式的. 什么是观察者模式: 定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新. 示例: GUI编程中的addXxxxListener都是观察者模式. 比如为按钮点击添加监听事件,为键盘添加监听等等- 观察者模式的三个重要类: 被监听的事件源,也就是我们在使用的对象. 注册的那个监听器,是专门用来监听当前使用的对象的.

  • 快速学习JavaWeb中监听器(Listener)的使用方法

    一.监听器介绍 1.1.监听器的概念 监听器是一个专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动.监听器其实就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法立即被执行. 1.2.监听器案例--监听window窗口的事件监听器 package me.gacl.listener.demo; import java.awt.Frame; imp

  • Filter、Servlet、Listener的学习_动力节点Java学院整理

    Java中Filter.Servlet.Listener的学习资料,希望大家喜欢 1.Filter的功能 filter功能,它使用户可以改变一个 request和修改一个response. Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,也可以在离开 servlet时处理response.换种说法,filter其实是一个"servlet chaining"(servlet 链). 一个Filte

  • Java listener简介_动力节点Java学院整理

    1: 监听器的定义: 监听器实际上是一个类,这个类实现了特定的接口,然后将这个类在 web.xml 文件中进行描述,这样服务器在启动的时候就可以实例化这个类,启动监听器.当范围对象的状态发生变化的时候,服务器自动调用监听器对象中的方法.例如统计用户在线人数. web监听器是Servlet规范中定义的一种特殊类,用于监听ServletContext,HttpSession,ServletRequest等域对象的创建.销毁.以及属性的变化等,可以在事件发生前.发生后进行一些处理. 2.监听器的用途

  • Java servlet、filter、listener、interceptor之间的区别和联系

    servlet.filter.listener.interceptor之间的区别和联系 一.概念 1.servlet:servlet是一种运行服务器端的java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层. 2.filter:filter是一个可以复用的代码片段,可以用来转换HTTP请求.响应和头信息.Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应. 3.listener:

  • 深入学习JavaWeb中监听器(Listener)的使用方法

    一.监听域对象中属性的变更的监听器 域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器. 这三个监听器接口分别是ServletContextAttributeListener, HttpSessionAttributeListener 和ServletRequestAttributeListener,这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换

  • 详解JavaWeb中的 Listener

    一.基本概念 JavaWeb里面的listener是通过观察者设计模式进行实现的.对于观察者模式,这里不做过多介绍,大概讲一下什么意思. 观察者模式又叫发布订阅模式或者监听器模式.在该模式中有两个角色:观察者和被观察者(通常也叫做主题).观察者在主题里面注册自己感兴趣的事件,当这个事件发生时,主题会通过回调接口的方式通知观察者. 举个生活中的例子:订阅报纸.任何一个家庭或个人都可以向报社订阅报纸.这里报社就是"主题",家庭就是"观察者".比如家庭需要订阅明天早晨的报

  • 详解javaweb中jstl如何循环List中的Map数据

    详解javaweb中jstl如何循环List中的Map数据 第一种方式: 1:后台代码(测试) List<Map<String, Object>> list = new ArrayList<Map<String,Object>>(); Map<String, Object> map = null; for (int i = 0; i < 4; i++) { map = new HashMap<String, Object>();

  • 详解JavaWeb中的过滤器Filter

    一.什么是过滤器 1.Filter过滤器的作用:拦截请求 2.拦截请求常见场景: (1)权限检查 (2)日记操作 (3)事务管理 1.1 使用步骤 Filter 过滤器的使用步骤: 1.编写一个类去实现Filter 接口 2.实现过滤方法doFilter() 3.到web.xml 中去配置Filter 的拦截路径 二.初体验 web工程下新建一个admin目录,作为需要权限才能访问的目录,其中有两个文件 2.1 mynav.html <!DOCTYPE html> <html lang=

  • 详解java中的PropertyChangeSupport与PropertyChangeListener

    详解java中的PropertyChangeSupport与PropertyChangeListener java中的PropertyChangeSupport与PropertyChangeListener配合使用能够实现,绑定属性会在属性值发生变化时,通知所有相关的监听器. javaBean: package com.wang.test.property; import java.beans.PropertyChangeListener; import java.beans.PropertyC

  • 详解Android中获取软键盘状态和软键盘高度

    详解Android中获取软键盘状态和软键盘高度 应用场景 在Android应用中有时会需要获取软键盘的状态(即软键盘是显示还是隐藏)和软键盘的高度.这里列举了一些可能的应用场景. 场景一 当软键盘显示时,按下返回键应当是收起软键盘,而不是回退到上一个界面,但部分机型在返回键处理上有bug,按下返回键后,虽然软键盘会自动收起,但不会消费返回事件,导致Activity还会收到这次返回事件,执行回退操作,这时就需要判断,如果软键盘刚刚由显示变为隐藏状态,就不执行回退操作. 场景二 当软键盘弹出后,会将

  • 详解IDEA中SpringBoot整合Servlet三大组件的过程

    Spring MVC整合 SpringBoot提供为整合MVC框架提供的功能特性 内置两个视图解析器:ContentNegotiatingViewResolver和BeanNameViewResolver 支持静态资源以及WebJars 自动注册了转换器和格式化器 支持Http消息转换器 自动注册了消息代码解析器 支持静态项目首页index.html 支持定制应用图标favicon.ico 自动初始化Web数据绑定器:ConfigurableWebBindingInitializer Sprin

  • 详解JavaWeb如何实现文件上传和下载功能

    目录 1. 文件传输原理及介绍 2. JavaWeb文件上传 2.1我们用一个新的方式创建项目 2.2 导包 2.3 实用类介绍 2.4 pom.xml导入需要的依赖 2.5 index.jsp 2.6 info.jsp 2.7 FileServlet 2.8 配置Servlet 2.9 测试结果 3. SpringMVC文件上传和下载 3.1 上传 3.2 下载 1. 文件传输原理及介绍 2. JavaWeb文件上传 2.1我们用一个新的方式创建项目 空项目会直接弹出框 把jdk版本设置好 点

  • 详解Flutter中视频播放器插件的使用教程

    目录 创建一个新的视频播放器 添加播放和暂停按钮 创建一个快进 添加一个视频进度指示器 应用视频的字幕 结论 您已经看到很多包含视频内容的应用程序,比如带有视频教程的食谱应用程序.电影应用程序和体育相关的应用程序.您是否想知道如何将视频内容添加到您的下一个Flutter应用程序中? 从头开始实现视频功能将是一项繁重的任务.但有几个插件可以让开发者的生活变得轻松.视频播放器插件是可用于 Flutter 的最佳插件之一,可满足这一要求. 在这篇文章中,您将学习如何应用视频播放器插件以及控制视频播放器

  • Java详解Swing中的几种常用按钮的使用

    目录 Swing中的常用按钮 AbstractButton的常用方法 JRadionButton(单选按钮) 单选按钮的构造方法 复选框(JCheckBox) 复选框的构造方法 组合框(JComboBox) 组合框的构造方法 下拉列表框的常用方法 小结 Swing中的常用按钮 在Swing中,常见的按钮组件有JButton,JCheckBox,JRadioButton等,它们都是抽象类AbstractButton类的直接或间接子类.在AbstractButton类中提供了按钮组件通用的一些方法.

  • 一文详解Golang中net/http包的实现原理

    目录 前言 http包执行流程 http包源码分析 端口监听 请求解析 路由分配 响应处理 前言 Go语言自带的net/http包提供了HTTP客户端和服务端的实现,实现一个简单的http服务非常容易,其自带了一些列结构和方法来帮助开发者简化HTTP服务开发的相关流程,因此我们不需要依赖任何第三方组件就能构建并启动一个高并发的HTTP服务器,net/http包在编写web应用中有很重要的作用,这篇文章会学习如何用 net/http 自己编写实现一个 HTTP Server 并探究其实现原理,具体

随机推荐