之前学习了很多涉及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. Luogu4528 CTSC2008 图腾 树状数组、容斥

    传送门 设$f_i$表示$i$排列的数量,其中$x$表示不确定 那么$$ans=f_{1324}-f_{1432}-f_{1243}=(f_{1x2x}-f_{1423})-(f_{14xx}-f_{ ...

  2. DeskMini无传统机械键盘与鼠标接口的情况下使用U盘安装系统经验总结

    总结安装纯净版Win7旗舰版系统安装过程所解决的问题要点: 1:UEFI引导启动的实现. 2:使用Dism++实现系统的安装. 3:使用Dism++解决新主板在安装系统过程中不能使用USB键盘和鼠标的 ...

  3. 【LGR-048 五周年庆贺】洛谷6月月赛

    Luogu的五周年庆典比赛,还是比较满意的. 题目清新不毒瘤,数据优质不卡常,解法自然,为出题人点赞. 前三题的难度都很低,T5个人感觉还好.但是最后那个splay+hash是什么神仙东西. 最后好像 ...

  4. 记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)

    0x01 前言 最近在做代码审计的工作中遇到了一个难题,题目描述如下: <?php include 'flag.php'; if(isset($_GET['code'])){ $code = $ ...

  5. open-falcon ---安装Dashboard时候报错"SSLError: The read operation timed out"

    在部署open-falcon环境过程中,安装Dashboard时候报错"SSLError: The read operation timed out".如下: [root@open ...

  6. 树的最长链-POJ 1985 树的直径(最长链)+牛客小白月赛6-桃花

    求树直径的方法在此转载一下大佬们的分析: 可以随便选择一个点开始进行bfs或者dfs,从而找到离该点最远的那个点(可以证明,离树上任意一点最远的点一定是树的某条直径的两端点之一:树的直径:树上的最长简 ...

  7. 《移山之道》Reading Task——by12061154Joy

    最近因为作业的原因所以接触到了这本书,给我最特别的感觉就是很新鲜,主要是因为这本书是以故事展开的,大概是我读的书太少,基本没有看到过专业书的知识体系是用故事串讲起来的,这样帮助读者理解了一些概念并且不 ...

  8. Linux实践四:ELF文件格式分析

    一.分析ELF文件头 二.通过文件头找到section header table,理解内容 三.通过section header table 找到各section 四.理解常见.text .strta ...

  9. Windows10安装ubuntu & caffe GPU版

    1.Ubuntu https://www.cnblogs.com/EasonJim/p/7112413.html https://blog.csdn.net/jesse_mx/article/deta ...

  10. javascript数据类型以及类型间的转化函数

    js 有五种基本数据类型,还有个引用类型 1.undefined 类型,只有一个志undefined 当变量未初始化时都会是这个类型. 2.null 类型,也是只有一个值null,null类型的typ ...