热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

详解JavaWeb中的Listener

JavaWeb里面的listener是通过观察者设计模式进行实现的。下面通过本文给大家详细介绍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


com.nantang.listener.TestListener

当容器启动时会向日志中输出"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已经增加了一些事件监听器接口,道理都是类似的,读者可以自行去了解。


推荐阅读
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • Hibernate延迟加载深入分析-集合属性的延迟加载策略
    本文深入分析了Hibernate延迟加载的机制,特别是集合属性的延迟加载策略。通过延迟加载,可以降低系统的内存开销,提高Hibernate的运行性能。对于集合属性,推荐使用延迟加载策略,即在系统需要使用集合属性时才从数据库装载关联的数据,避免一次加载所有集合属性导致性能下降。 ... [详细]
  • 使用J2SE模拟MVC模式开发桌面应用程序的工程包的介绍
    以我开发过的一个娱乐管理系统为例:下图为我系统的业务逻辑的MVC流程:下图为以Eclipse开发中各包的说明:转载于:https:blog ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • 项目运行环境配置及可行性分析
    本文介绍了项目运行环境配置的要求,包括Jdk1.8、Tomcat7.0、Mysql、HBuilderX等工具的使用。同时对项目的技术可行性、操作可行性、经济可行性、时间可行性和法律可行性进行了分析。通过对数据库的设计和功能模块的设计,确保系统的完整性和安全性。在系统登录、系统功能模块、管理员功能模块等方面进行了详细的介绍和展示。最后提供了JAVA毕设帮助、指导、源码分享和调试部署的服务。 ... [详细]
  • Struts2+Sring+Hibernate简单配置
    2019独角兽企业重金招聘Python工程师标准Struts2SpringHibernate搭建全解!Struts2SpringHibernate是J2EE的最 ... [详细]
author-avatar
不完整的记忆721_560
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有