欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解JavaWeb中的 Listener

程序员文章站 2024-03-13 13:18:57
一、基本概念 javaweb里面的listener是通过观察者设计模式进行实现的。对于观察者模式,这里不做过多介绍,大概讲一下什么意思。 观察者模式又叫发布订阅模式或者...

一、基本概念

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

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

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

详解JavaWeb中的 Listener

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

详解JavaWeb中的 Listener

二、样例分析

上面说了,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应用的。下面是这两个接口的类图层级关系。

 详解JavaWeb中的 Listener

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相关监听接口

详解JavaWeb中的 Listener

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相关监听接口

详解JavaWeb中的 Listener

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已经增加了一些事件监听器接口,道理都是类似的,读者可以自行去了解。