在Java Web程序中使用监听器可以通过以下两种方法
之前学习了很多涉及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程序中使用监听器可以通过以下两种方法的更多相关文章
- web页面中快速找到html对应元素两种方法
一.第一种方法(通过先进入开发模式然后再去选择网页元素) 1.打开IE.Chrome.FireFox等,按 F12 键进入开发模式 2.在打开的控制窗口左上角有个 箭头 按钮,点击它之后,此时将鼠标 ...
- JAVA读取Excel中内容(HSSF和Workbook两种方法)
内容添加,以前是用的HSSF,前几天帮同学写一个统计表用了Workbook,现在码一下. ---新内容(Workbook)--- 同学要统计一个xls表格,让表1里面的某一列内容对表2里面的每列进行匹 ...
- 在Java Web程序中使用Hibernate
在Java Web程序中使用Hibernate与普通Java程序一样.本文中将使用Servlet和JSP结合Hibernate实现数据库表的增删改查操作. Web程序中,hibernate.cfg.x ...
- Java 获取*.properties配置文件中的内容 ,常见的两种方法
import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.Pr ...
- WPF程序将DLL嵌入到EXE的两种方法
WPF程序将DLL嵌入到EXE的两种方法 这一篇可以看作是<Visual Studio 版本转换工具WPF版开源了>的续,关于<Visual Studio 版本转换工具WPF版开源了 ...
- QT中获取选中的radioButton的两种方法(动态取得控件的objectName之后,对名字进行比较)
QT中获取选中的radioButton的两种方法 QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioBu ...
- js如何实现动态的在表格中添加和删除行?(两种方法)
js如何实现动态的在表格中添加和删除行?(两种方法) 一.总结 1.table元素有属性和一些方法(js使用) 方法一:添加可通过在table的innerHTML属性中添加tr和td来实现 tab.i ...
- web项目中实现页面跳转的两种方式
<a href="javascript:"></a>跳转在网页本身,URL不改变 <a href="#"></a> ...
- 在web page中使鼠标右击失效的几种方法
这里主要介绍两种方法,一种是使用js来处理,还有一种是在html属性中设置. 方法一:js 1: <script language="javascript"> docu ...
随机推荐
- eclipse 打包
add directory entries 不勾选, spring 自动扫描之类的扫描不到
- [Spark][Python]DataFrame中取出有限个记录的例子
[Spark][Python]DataFrame中取出有限个记录的例子: sqlContext = HiveContext(sc) peopleDF = sqlContext.read.json(&q ...
- [Oracle]快速生成大量模拟数据的方法
快速生成大量模拟数据的方法: create table TEST(id integer, TEST_NUMBER NUMBER(18,6)); insert into TEST select i+j, ...
- (译)理解 LSTM 网络 (Understanding LSTM Networks by colah)
@翻译:huangyongye 原文链接: Understanding LSTM Networks 前言:其实之前就已经用过 LSTM 了,是在深度学习框架 keras 上直接用的,但是到现在对LST ...
- bootstrapTable使用场景及方式
场景一:混合开发,适用jsp.php前端不足 纯html结构 <div> <table id="table" data-toggle="table&q ...
- RabbitMQ 发布订阅-实现延时重试队列(参考)
RabbitMQ消息处理失败,我们会让失败消息进入重试队列等待执行,因为在重试队列距离真正执行还需要定义的时间间隔,因此,我们可以将重试队列设置成延时处理.今天参考网上其他人的实现,简单梳理下消息延时 ...
- 新人入坑Redis必会的吐血总结
新人入坑Redis必会的吐血总结 一.什么是Redis Redis是一个使用C语言开发的开源的高性能的key-value存储系统,我们可以把它近似理解为Java Map.简单来讲,Redis是一种NO ...
- SpringMVC环境搭建——HelloWorld
1.新建Maven Web 工程: 2.添加相关的依赖包(Spring MVC.tomcat插件等),具体的pom.xml文件如下 <project xmlns="http://mav ...
- 《Linux内核设计与实现》读书笔记 3
第三章 进程管理 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于程序,还包含其他资源(打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内催音社的内存地址空间及一个或多个执行线 ...
- 查看电脑保存的wifi密码
查看电脑保存的wifi密码 查看电脑链接过的WiFinetsh wlan show profile 查看wifi的密码netsh wlan show profile name=8888 key=cle ...