在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 ...
随机推荐
- 一篇文章让你彻底掌握 shell 语言
一篇文章让你彻底掌握 shell 语言 由于 bash 是 Linux 标准默认的 shell 解释器,可以说 bash 是 shell 编程的基础. 本文主要介绍 bash 的语法,对于 linux ...
- Python 3下Matplotlib画图中文显示乱码的解决方法
解决办法: 因为乱码是Matplotlib缺少中文配置所导致的,所以我们只需要在程序中说明使用中文字体即可. 先选一个字体.在计算机中找到字体,选择一种中文字体,比如我这里用的是楷体 右键可以查看其属 ...
- Luogu3959 NOIP2017 宝藏 状压DP
题目传送门:https://www.luogu.org/problemnew/show/P3959 题意:给出一个有$N$个点的图,求其中的一个生成树(指定一个点为根),使得$\sum\limits_ ...
- Data Partitioning Guidance
在很多大规模的解决方案中,数据都是分成单独的分区,可以分别进行管理和访问的.而分割数据的策略必须仔细的斟酌才能够最大限度的提高效益,同时最大限度的减少不利影响.数据的分区可以极大的提升可扩展性,降低争 ...
- HNOI2018简要题解
HNOI2018简要题解 D1T1 寻宝游戏 题意 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为 ...
- MongoDB集群运维笔记
前面的文章介绍了MongoDB副本集和分片集群的做法,下面对MongoDB集群的日常维护操作进行小总结: MongDB副本集故障转移功能得益于它的选举机制.选举机制采用了Bully算法,可以很方便从分 ...
- [linux] VirtualBox复制虚拟机
环境: Oracle VM VirtualBox 5.0.20 CentOS-6.7-x86_64-minimal.iso 1.复制虚拟机 -->右击休眠状态模板虚拟机,选择复制 -->填 ...
- CF 910 C. Minimum Sum
链接 [http://codeforces.com/group/1EzrFFyOc0/contest/910/problem/C] 题意 给你n个字符串,每个字符串的字符是a~j,每个字符都可以是0~ ...
- Python学习笔记(三)——条件语句、循环语句
注:需注意代码的缩进格式 注:需注意代码的缩进格式 注:需注意代码的缩进格式 Python 与其他语言最大的区别就是,Python 的代码块不使用大括号 {} 来控制类,函数以及其他逻辑判断.pyth ...
- JQuery Cross Domain Ajax(jsonp)
http://www.pureexample.com/jquery/cross-domain-ajax.html http://www.pureexample.com/ExampleTesterII- ...