找回密码
 FreeOZ用户注册
查看: 1427|回复: 11
打印 上一主题 下一主题

Listener技术在Servlet环境下的应用 —— 小key出品

[复制链接]
跳转到指定楼层
1#
发表于 14-7-2009 21:23:46 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
在spring横行的今天,Servlet的一些基本技术可能不被人们重视。
其实在Servlet中还是有一些很有意思的技术细节的,比如listener就是
其中之一。

本文的目的是讨论一下Servlet环境下如何配置和使用相关的listener
回复  

使用道具 举报

2#
 楼主| 发表于 14-7-2009 21:35:48 | 只看该作者

事件与java.util.EventObject

事件,Event,是一个GUI编程的概念。所谓事件驱动,是指Windows编程时,
由一些相应的事件,如鼠标操作、按钮点击等,触发的操作。

在Java编程理念中,事件是被封装成一个事件对象。这种理念从AWT界面编程开始
就已经实现在Java语言中。

于是,从那时起,就有一个作为各种事件的公共父类:EventObject

在Web编程时代,事件依然起源于EventObject。这个对象很简单,但很实用,
它有一个带参数的构造函数,以及两个方法:
  1. # source : Object
  2. + EventObject(Object source)

  3. + getSource() : Object
  4. + toString() : String
复制代码
Source是事件源,就是由谁来触发这个事件。整个EventObject可以简单
描述成:谁做了什么 —— 这正好就是事件的意义
回复  

使用道具 举报

3#
 楼主| 发表于 14-7-2009 21:47:02 | 只看该作者

最简单的事件:生命周期事件对象

页面、请求、Session和Context构成了Servlet的四个处理层次。
(一般只针对JSP说页面)。

Context,即ServletContext,在同一个JVM环境中只有一个。
所以,Context是一个全局性的处理层次。

Session,如果没有显式指定<%@page session="false" %>,
每个用户连接到我们的Web App后,都有机会建立他们自己专用,
而同时在多个页面进行共享的session。这是一个紧次于Context的,
以用户为单位的全局层次。

Request则代表了同一次用户请求的处理过程。这是一个粒度比较
小的层次。

无论是Request还是Session还是Context,都有着自身的生命周期。
不同阶段的转换,会导致事件的产生。为了表证这些事件,我们建立
了事件对象:

ServletRequestEvent extends EventObject
HttpSessionEvent extends EventObject
ServletContextEvent extends EventObject
回复  

使用道具 举报

4#
 楼主| 发表于 14-7-2009 21:55:35 | 只看该作者

走近生命周期事件对象

从java.util.EventObject的构成,我们可以很容量想到:
这三个事件对象的source是谁?
  1. ServletRequestEvent -> ServletRequest -> + getServletRequest() : ServletRequest ***
  2. HttpSessionEvent -> HttpSession -> getSession() : HttpSession
  3. ServletContextEvent -> ServletContext -> getServletContext() : ServletContext
复制代码
其中 *** 处,即ServletRequestEvent是有错的,
事实上系统把ServletContext也作为了这个事件的源,下面是相关的源代码:
  1. 38     public ServletRequestEvent(ServletContext sc, ServletRequest request) {
  2. 39         super(sc);
  3. 40         this.request = request;
  4. 41     }
  5. 42
  6. 43     /**
  7. 44       * Returns the ServletRequest that is changing.
  8. 45       */
  9. 46     public ServletRequest getServletRequest () {
  10. 47         return this.request;
  11. 48     }
  12. 49
  13. 50     /**
  14. 51       * Returns the ServletContext of this web application.
  15. 52       */
  16. 53     public ServletContext getServletContext () {
  17. 54         return (ServletContext) super.getSource();
  18. 55     }
复制代码
回复  

使用道具 举报

5#
 楼主| 发表于 14-7-2009 22:02:57 | 只看该作者

属性变化而触发的事件

除了生命周期外,另一个很需要重视的事件是不同级别的属性触发的事件。

我们知道,Servlet有三个数据共享级别(这里不算页面一级),分别为:
Request
Session
Context

对应的类是:
ServletRequest/HttpServletRequest
HttpSession
ServletContext

它们都有共同的属性处理方法:
  1. + getAttribute(String name) : Object
  2. + getAttributeNames() : Enumeration
  3. + setAttribute(String name, Object value) : void
  4. + removeAttribute(String name): void
复制代码
由此可见,对于不同级别的属性,需要处理属性的添加、删除、和更新。
为此而定义了一系列属性变化能触发的事件类:
  1. ServletRequestAttributeEvent extends ServletRequestEvent
  2. HttpSessionBindingEvent extends HttpSessionEvent
  3. ServletContextAttributeEvent extends ServletContextEvent
复制代码
这里需要注意,并没有HttpSessionAttributeEvent,而是采用HttpSessionBindingEvent。
回复  

使用道具 举报

6#
 楼主| 发表于 14-7-2009 22:07:23 | 只看该作者

事件类层次图

回复  

使用道具 举报

7#
 楼主| 发表于 14-7-2009 22:33:12 | 只看该作者

响应器:都是接口

我这里把Listener称之为响应器。

所有的响应器都是接口。实现了对应的接口,就可以用来
响应某个事件了。这个概念对于有GUI编程经验的同学应该
不陌生。
回复  

使用道具 举报

8#
 楼主| 发表于 14-7-2009 22:37:13 | 只看该作者

响应器的鼻祖:java.util.EventListener

这是一个tagging interface。类似的tagging interface还有Serializable, Clonable.
回复  

使用道具 举报

9#
 楼主| 发表于 14-7-2009 22:43:16 | 只看该作者

生命周期响应器

有两个,一个是针对ServletContext的初始化和Destroy的。
ServletContextListener:
  + contextInitialized(ServletContextEvent event) : void
     初始化后即执行
  + contextDestroyed(ServletContextEvent event) : void
    准备destroy时执行

另一个是针对Session的,是创建session对象后和销毁session之前(session无效之后)执行:
HttpSessionListener
  + sessionCreated(HttpSessionEvent event) : void
  + sessionDestroyed(HttpSessionEvent event) : void

注意
如果在分布式环境中,就不要完全依靠这两个事件响应器来处理事件。
回复  

使用道具 举报

10#
 楼主| 发表于 14-7-2009 22:49:34 | 只看该作者

属性变化响应器

有三个,针对三个不同的共享层次:
ServletContextAttributeListener
  + attributeAdded(ServletContextAttributeEvent event) : void
  + attributeRemoved(ServletContextAttributeEvent event): void
  + attributeReplaced(ServletContextAttributeEvent event): void

HttpSessionAttributeListener
  + attributeAdded(HttpSessionBindingEvent event) : void
  + attributeRemoed(HttpSessionBindingEvent event) : void
  + attributeReplaced(HttpSessionBindingEvent event) : void

ServletRequestAttributeListener
  + attributeAdded(ServletRequestAttributeEvent event) : void
  + attributeRemoved(ServletRequestAttributeEvent event) : void
  + attributeReplaced(ServletRequestAttributeEvent event) : void

注意
这面的所有listener,包括楼上谈到的生命周期listener都有两个特点:
1. 在web.xml中配置
2. 不能在分布式环境中使用(ServletRequestAttributeListener除外)
回复  

使用道具 举报

11#
 楼主| 发表于 14-7-2009 22:52:56 | 只看该作者

listener类层次图

回复  

使用道具 举报

12#
 楼主| 发表于 14-7-2009 23:23:06 | 只看该作者

配置listener

在web.xml中配置listener是很简单的。<web-app>提供了<listener>子元素,它有且只有一个子元素,
<listener-class>:

<web-app>
  ...
  <listener>
    <listener-class>com.key.KeyServletContextListener</listener-class>
  </listener>
  ...
</web-app>

我们不妨看看Tomcat 6.0对于Listener配置的处理实现:
  1. 3896         // Sort listeners in two arrays
  2. 3897         ArrayList eventListeners = new ArrayList();
  3. 3898         ArrayList lifecycleListeners = new ArrayList();
  4. 3899         for (int i = 0; i < results.length; i++) {
  5. 3900             if ((results[i] instanceof ServletContextAttributeListener)
  6. 3901                 || (results[i] instanceof ServletRequestAttributeListener)
  7. 3902                 || (results[i] instanceof ServletRequestListener)
  8. 3903                 || (results[i] instanceof HttpSessionAttributeListener)) {
  9. 3904                 eventListeners.add(results[i]);
  10. 3905             }
  11. 3906             if ((results[i] instanceof ServletContextListener)
  12. 3907                 || (results[i] instanceof HttpSessionListener)) {
  13. 3908                 lifecycleListeners.add(results[i]);
  14. 3909             }
  15. 3910         }
  16. 3911
  17. 3912         setApplicationEventListeners(eventListeners.toArray());
  18. 3913         setApplicationLifecycleListeners(lifecycleListeners.toArray());
复制代码
这是org.apache.catalina.core.StandardContext中的代码。系统把所有事件响应器归成两类,
一类用是普通事件响应器,另一类是lifecycle响应器,分别存入两个数组中备用。

请注意,在Line-3906行没有采用else if,而是另起一个if,这样就可以使同一个响应器,如果实现了
两类接口,可以归入两个不同类别的数组。

同一个程序文件相邻位置就有对ServletContextListener的调用:
  1. 3920         Object instances[] = getApplicationLifecycleListeners();
  2. 3921         if (instances == null)
  3. 3922             return (ok);
  4. 3923         ServletContextEvent event =
  5. 3924           new ServletContextEvent(getServletContext());
  6. 3925         for (int i = 0; i < instances.length; i++) {
  7. 3926             if (instances[i] == null)
  8. 3927                 continue;
  9. 3928             if (!(instances[i] instanceof ServletContextListener))
  10. 3929                 continue;
  11. 3930             ServletContextListener listener =
  12. 3931                 (ServletContextListener) instances[i];
  13. 3932             try {
  14. 3933                 fireContainerEvent("beforeContextInitialized", listener);
  15. 3934                 listener.contextInitialized(event);
  16. 3935                 fireContainerEvent("afterContextInitialized", listener);
  17. 3936             } catch (Throwable t) {
  18. 3937                 fireContainerEvent("afterContextInitialized", listener);
  19. 3938                 getLogger().error
  20. 3939                     (sm.getString("standardContext.listenerStart",
  21. 3940                                   instances[i].getClass().getName()), t);
  22. 3941                 ok = false;
  23. 3942             }
  24. 3943         }
复制代码
以上的代码在listenerStart()方法中。在listenerStop()方法中,则有相应的contextDestroyed()方法的调用,
这里就没有细述的必要啦。
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 14-12-2024 20:29 , Processed in 0.046831 second(s), 29 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表