之前学习了很多涉及servlet的内容,本小结我们说一下监听器,说起监听器,编过桌面程序和手机App的都不陌生,常见的套路都是拖一个控件,然后给它绑定一个监听器,即可以对该对象的事件进行监听以便发生响应,从本质上来说这些都是观察者模式的具体实现,在web程序中的监听器也不例外。
在Java Web程序中使用监听器可以通过以下两种方法:
通过注解@WebListener来标识一个自定义的监听器;
[java] view plain copy
@WebListener
public class CustomListener implements Listener {

}
通过在web.xml中配置来使用监听器;
[html] view plain copy
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
在java Web程序中,主要是对ServletContext、HttpSession和ServletRequest三个对象提供支持。
一、 ServletContext监听器
有两类监听器用来监听ServletContext对象:
ServletContextListener:该监听器用来监听ServletContext的初始化和销毁事件;
ServletContextAttributeListener:该监听器用来监听ServletContext中属性的删除、添加和替换事件;
实例如下:
[java] view plain copy
@WebListener
public class MyServletListener implements ServletContextListener, ServletContextAttributeListener {
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContextEvent source: " + sce.getSource(www.leyujiada.cn));
System.out.println("context created");
}

public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContextEvent source: " + sce.getSource());
System.out.println("context destroyed");
}

public void attributeAdded(ServletContextAttributeEvent event) {
System.out.println("ServletContextAttributeEvent source: " + event.getSource());
System.out.println("www.zjingjiyl.cn attribute added: " + event.getName() + "--" + event.getValue());
}

public void attributeRemoved(ServletContextAttributeEvent event) {
System.out.println("ServletContextAttributeEvent source: " + www.txfenfenc11.cn event.getSource());
System.out.println("attribute removed: "+ event.getName() + "--" + event.getValue());
}

public void attributeReplaced(ServletContextAttributeEvent event) {
System.out.println("ServletContextAttributeEvent source: " + www.lieqibiji.com/ event.getSource());
System.out.println("attribute replaced: www.chuangyed.com "+ event.getName() + "--" + event.getValue());
}
}
该类实现了上述的两个接口,启动Servlet容器,分析结果,输出如下:

可以看到在启动的过程中首先创建了ServletContext,这激发了contextInitialized监听器方法,然后调用了每一个Servlet的init()方法对每一个Servlet进行初始化,对所有的Servlet(这里有两个自定义Servlet,MyServlet,TestServlet)初始化完成后则会向ServletContext添加一个属性,不管是事件对象还是添加的属性都是Servlet容器提供的。
当关闭Servlet容器的时候,分析结果,输出如下:

首先会调用所有Servlet的destroy方法,然后销毁ServletContext实例。
在对属性进行增加、替换、删除时,会调用相应的监听器,代码如下:
[java] view plain copy
ServletContext context = req.getServletContext();
context.setAttribute("country", "zn");
context.setAttribute("country", "en");
context.removeAttribute("country");
代码输出:

这里要注意的是:当替换属性时也是使用setAttribute(),只不过是key的值是相同的。当替换属性后,监听器得到的值是旧值(我觉得旧值和新值都应提供相应API)。
二、 HttpSession监听器
有四类监听器用来监听HttpSession对象:
HttpSessionListener:用来监听HttpSession的创建和销毁;
HttpSessionActivationListener:这个监听器是在分布式环境下使用的,当HttpSession实例要在不同的虚拟机之间进行迁移或者序列化时则会用到这个类,当HttpSession对象被迁移到新的虚拟机上时或者反序列化时则会调用sessionDidActivate()方法,当HttpSession实例从旧的虚拟机迁出或者序列化时则会调用sessionWillPassivate()方法;
HttpSessionAttributeListener:用来监听HttpSession中属性的删除、增加和替换事件;
HttpSessionBindingListener:
其实在新出来的Servlet3.1规范中还有一个Session监听器,javax.servlet.http.HttpSessionIdListener用于监听SessionId的改变,由于我用的是Tomcat7还不支持Servlet3.1所以会找不到这个类,所以这里就没写,如果有兴趣的可以自己尝试一下。
1、 HttpSessionListener和HttpSessionAttributeListener:
[java] view plain copy
@WebListener
public class MyHttpSessionListener implements HttpSessionListener, HttpSessionAttributeListener {
private Long startTime;

public void attributeAdded(HttpSessionBindingEvent event) {
startTime = (Long) event.getSession (www.tips139.com/).getAttribute("startTime");

System.out.println("HttpSessionBindingEvent source: " + event.getSource());
System.out.println("add attribute: " + event.getName() + "--" + event.getValue());
}

public void attributeRemoved(HttpSessionBindingEvent event) {
System.out.println("HttpSessionBindingEvent source: " + event.getSource());
System.out.println("remove attribute: " + event.getName() + "--" + event.getValue());
}

public void attributeReplaced(HttpSessionBindingEvent event) {
System.out.println("HttpSessionBindingEvent source: " + event.getSource());
System.out.println("replace attribute: " + event.getName() + "--" + event.getValue());
}

public void sessionCreated(HttpSessionEvent se) {
System.out.println("HttpSessionEvent source: " + se.getSource());
System.out.println("session id: " + se.getSession().getId());
System.out.println("session create");
}

public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("HttpSessionEvent source: " + se.getSource());
System.out.println("session id: " + se.getSession().getId());
System.out.println("active time: " + (System.nanoTime() - startTime));
System.out.println("session destroy");
}

}
对应的Servlet如下:
[java] view plain copy
@WebServlet(name="myHttpSessionServlet", urlPatterns="/myHttpSessionServlet", loadOnStartup=1)
public class MyHttpSessionServlet extends HttpServlet {
private static final long serialVersionUID = 5687825632187950599L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.setAttribute("startTime", System.nanoTime());

session.setAttribute("visitTimes", 0);
session.setAttribute("visitTimes", 1);
session.removeAttribute("visitTimes");

session.setMaxInactiveInterval(10);
resp.getWriter().write("session listene demo");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

}
}
第一次请求该网站的资源时,输出如下:

可以看出第一次首先创建HttpSession,调用了sessionCreated()方法。之后是添加方法时触发了对应的属性监听器,替换属性时还是监听器返回的替换前的值。
由于设置了存活时间,之后会触发销毁Session的方法,输出如下:

从中可见,Session销毁时不是只是调用sessionDestroyed()方法,在执行完这个方法之后,还会调用attributeRemoved()方法,将其中保存的所有属性全部清除。
2、 HttpSessionActivationListener
该监听器就像上面说过的一样,是在分布式环境下或者当要序列HttpSession实例时才起作用。
由于Session是存储在内存中的,所以有时候服务器为了节省内存,会把访问较少的Session 持久化到存储设备中,包括文件系统和数据库,这样就能节省内存空间,另一方面也告诉我们不要在Session中放过多的数据,以免造成服务器的负担;
当在分布式环境下时,如果一台机器发生宕机不能访问,可以将Session序列化进行传输到别的服务器中的JVM中进行工作,这其中就要求存储在Session中对数据要可以序列化,这时候是会使用到这个监听器的;
对Session进行序列化一般是Servlet容器进行的,我们可以进行配置;
ERROR,错误使用:
[java] view plain copy
HttpSession session = req.getSession();
session.setAttribute("startTime", System.nanoTime());
session.setAttribute("visitTimes", 0);
session.setAttribute("visitTimes", 1);
session.removeAttribute("visitTimes");
ObjectOutputStream output =new ObjectOutputStream(new FileOutputStream("C:\\demo"));
output.writeObject(session);
上述是错误的使用办法,不能序列化HttpSession,此处的序列化只是将Session实例中的信息转换成能在网络中传输的数据形式即可,含义范围要大于Java中的序列化概念,一般的理解是将对象转化成字符串。
至于Session为什么序列化,这就很明显了,如果服务器宕机,客户访问服务器的所有信息因为存在于内存中,所以也就全部丢失了,将Session序列化到存储设备之后可以在服务器恢复运行之后将用户信息也同时恢复。
3、 HttpSessionBindingListener
首先这个监听器的作用和之前的HttpSessionAttributeListener很像,使用起来也很类似,但是它们有如下区别:
HttpSessionAttributeListener要进行注册,或者通过注解或者通过web.xml,它是用来监听整个应用程序中的所有的Session实例,和所有Session实例对应的属性,属于一个通用的监听器;
HttpSessionBindingListener则是针对于一个特定的Session和一个特定的属性的监听器,是量身定制的,并且它也不需要进行注册;
使用实例如下:
[java] view plain copy
public class Person implements Serializable, HttpSessionBindingListener{
private static final long serialVersionUID = -4972586177179694554L;
private String name;
private Integer age;

public Person(){}

public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("person bound");
}
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("person unbound");
}
向doGet()方法中的Session中添加属性:
[java] view plain copy
session.setAttribute("person", new Person("lmy", 23));
session.setMaxInactiveInterval(10);
发出请求之后,输出如下:
[java] view plain copy
HttpSessionBindingEvent source: org.apache.catalina.session.StandardSessionFacade@fdfaab5
person bound
10秒之后Session失效,输出:
[java] view plain copy
HttpSessionBindingEvent source: org.apache.catalina.session.StandardSessionFacade@fdfaab5
person unbound
三、 ServletRequest监听器
有三类监听器用来监听ServletRequest对象:
ServletRequestListener:用来监听ServletRequest的创建和销毁,注意Servlet容器是通过池来重用ServletRequest的,创建一个ServletRequest实例其实就是从池中取出一个实例,销毁该对象也就是将该实例放回池中;
ServletRequestAttributeListener:用来监听ServletRequest中属性的删除、增加和替换事件;
AsyncListener:这个后面提到异步编程时再详细说;
监听器代码如下:
[java] view plain copy
@WebListener
public class MyServletRequestListener implements ServletRequestListener, ServletRequestAttributeListener {

public void attributeAdded(ServletRequestAttributeEvent srae) {
System.out.println("ServletRequestAttributeEvent source: " + srae.getSource());
System.out.println("request attribute added: " + srae.getName() + "--" + srae.getValue());
}

public void attributeRemoved(ServletRequestAttributeEvent srae) {
System.out.println("ServletRequestAttributeEvent source: " + srae.getSource());
System.out.println("request attribute removed: " + srae.getName() + "--" + srae.getValue());
}

public void attributeReplaced(ServletRequestAttributeEvent srae) {
System.out.println("ServletRequestAttributeEvent source: " + srae.getSource());
System.out.println("request attribute replaced: " + srae.getName() + "--" + srae.getValue());
}

public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("ServletRequestEvent source: " + sre.getSource());
System.out.println("request destroy");
}

public void requestInitialized(ServletRequestEvent sre) {
System.out.println("ServletRequestEvent source: " + sre.getSource());
System.out.println("request init");
}
}
MyHttpServlet1类接受请求代码:
[java] view plain copy
</pre><pre name="code" class="java">@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("MyHttpServlet1 request work");
<span style="white-space:pre"> </span>System.out.println("current thread :" + Thread.currentThread().getName());
}
[java] view plain copy
</pre><div>MyHttpServlet类接受请求代码:</div><pre name="code" class="java"><pre name="code" class="java">@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("MyHttpServlet request work");
<span style="white-space:pre"> </span>System.out.println("current thread :" + Thread.currentThread().getName());
}
向MyHttpServlet发送一个请求输出如下:

可以看出接收一个请求后首先初始化ServletRequest对象,调用requestInitialized()方法,之后会发现进行了属性的替换,使能异步处理,在之后进行处理请求的具体工作,最后销毁该请求对象。
向MyHttpServlet1发送一个请求输出如下:

处理的流程和向MyHttpServlet发送请求相同但是看出来请求的处理线程是不同的。
在请求的过程中添加属性和ServletContext中的对属性的监听器是类似的:
[java] view plain copy
req.setAttribute("area", "zh");
req.setAttribute("area", "ts");
req.removeAttribute("area");
输出如下:

属性替换时也是返回的替换之前的值,和ServletContext中属性的替换是相同的。

相关文章:
How are Threads allocated to handle Servlet request?
Java序列化(一)
HttpSessionActivationListener Example Use Case
理解Java对象序列化
java tomcat实现Session对象的持久化原理及配置方法介绍
Using Sessions and Session Persistence
Practical Usage of HttpSessionBindingListener And HttpSessionAttributeListener
What is difference between HttpSessionAttributeListener and HttpSessionBindingListener?

在Java Web程序中使用监听器可以通过以下两种方法的更多相关文章

  1. web页面中快速找到html对应元素两种方法

    一.第一种方法(通过先进入开发模式然后再去选择网页元素) 1.打开IE.Chrome.FireFox等,按 F12 键进入开发模式 2.在打开的控制窗口左上角有个  箭头 按钮,点击它之后,此时将鼠标 ...

  2. JAVA读取Excel中内容(HSSF和Workbook两种方法)

    内容添加,以前是用的HSSF,前几天帮同学写一个统计表用了Workbook,现在码一下. ---新内容(Workbook)--- 同学要统计一个xls表格,让表1里面的某一列内容对表2里面的每列进行匹 ...

  3. 在Java Web程序中使用Hibernate

    在Java Web程序中使用Hibernate与普通Java程序一样.本文中将使用Servlet和JSP结合Hibernate实现数据库表的增删改查操作. Web程序中,hibernate.cfg.x ...

  4. Java 获取*.properties配置文件中的内容 ,常见的两种方法

    import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.Pr ...

  5. WPF程序将DLL嵌入到EXE的两种方法

    WPF程序将DLL嵌入到EXE的两种方法 这一篇可以看作是<Visual Studio 版本转换工具WPF版开源了>的续,关于<Visual Studio 版本转换工具WPF版开源了 ...

  6. QT中获取选中的radioButton的两种方法(动态取得控件的objectName之后,对名字进行比较)

    QT中获取选中的radioButton的两种方法   QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioBu ...

  7. js如何实现动态的在表格中添加和删除行?(两种方法)

    js如何实现动态的在表格中添加和删除行?(两种方法) 一.总结 1.table元素有属性和一些方法(js使用) 方法一:添加可通过在table的innerHTML属性中添加tr和td来实现 tab.i ...

  8. web项目中实现页面跳转的两种方式

    <a href="javascript:"></a>跳转在网页本身,URL不改变 <a href="#"></a> ...

  9. 在web page中使鼠标右击失效的几种方法

    这里主要介绍两种方法,一种是使用js来处理,还有一种是在html属性中设置. 方法一:js 1: <script language="javascript"> docu ...

随机推荐

  1. ASP HTMLEncode/HTMLDecode

    asp 有Server.HTMLEncode 却没有 Server.HTMLDecode....... 需要自定义一个 HTMLDecode 函数: Function HTMLDecode(sText ...

  2. java线程池和中断总结

    目录 java线程池和中断总结 一. 线程池的使用 二. java中断机制 中断的处理 三. 线程间通信机制总结 java线程池和中断总结 本系列文是对自己学习多线程和平时使用过程中的知识梳理,不适合 ...

  3. 51Nod 1677 treecnt

    一道比较基础的计数题,还是一个常用的单独计算贡献的例子. 首先看题目和范围,暴力枚举肯定是不可行的,而且\(O(n\ logn)\)的算法貌似很难写. 那我们就来想\(O(n)\)的吧,我们单独考虑每 ...

  4. DELL升级bios方式

    升级过程其实很简单 1.官网下载exe文件,放入U盘 2.重启电脑F12选择升级bios 3.选择U盘里面的升级文件 4.重点来了!!! boot options 是什么? 就是boot设置里面自己自 ...

  5. 使用IntelliJ IDEA开发SpringMVC网站(二)框架配置

    原文:使用IntelliJ IDEA开发SpringMVC网站(二)框架配置 摘要 讲解如何配置SpringMVC框架xml,以及如何在Tomcat中运行 目录[-] 文章已针对IDEA 15做了一定 ...

  6. 对于windows 10使用感受

    windows 10是美国微软公司研发的新一代跨平台及设备应用的操作系统.在2015年7月29日12点起,windows 10推送全面开始,windows 7.windows 8 用户可以升级到win ...

  7. 《Linux内核分析》第六周学习总结

    <Linux内核分析>第六周学习总结                         ——进程的描述和进程的创建 姓名:王玮怡  学号:20135116 一.理论部分 (一)进程的描述 1 ...

  8. Oracle的安装与配置

    好久不来博客园了,有种熟悉而又陌生的感觉. 今天我装一下Oracle数据库,从头开始,因为昨天在虚拟机装了,不能用,卸掉了,系统也卸掉了,今天重新装,包括系统. 系统装好了,Oracle准备好了. 这 ...

  9. 软件工程(GZSD2015) 第二次作业进度

    贵州师范大学软件工程第二次作业 徐 镇 王铭霞 张 英 涂江枫 张 燕 安 坤 周 娟 杨明颢 杨家堂 罗文豪 娄秀盛 周 娟 李盼 岳庆 张颖 李丽思 邓婷 唐洁 郑倩 尚清丽 陈小丽 毛茸 宋光能 ...

  10. Hadoop技术里面有BSP模型、MPI模型

    MPI模型,各种编程语言的库挺多. BSP模型,刚才知道.