[09] 监听器 Listener
1、事件
1.1 事件的概念
在Servlet中有一个概念叫做监听,顾名思义,就是监听某种事件是否发生。就如你是一家娱乐媒体公司的老板,你派出狗仔队去跟着某些明星,比如你想了解他们的绯闻,或者活动进展情况。这里的绯闻和活动进展情况,就是所谓的事件。
![图片搜索结果](https://images2018.cnblogs.com/blog/1007017/201806/1007017-20180617005401683-661597789.png)
在Web编程中,某些操作总会触发一种事件的发生,如启动或关闭容器、创建和销毁对话等。我们说过,Java是面向对象的语言,所以当发生了某种事件,容器将会创建对应的事件类对象。
也就是说,API中已经定义好了事件的类型,容器进行了实现,当某些特定操作发生时,会自动触发相应的事件。
1.2 事件类型
Servlet API中定义了6种事件类型:
- ServletContextEvent
- 该类表示上下文事件,当应用上下文对象发生改变,如创建或销毁上下文对象时,将触发上下文事件
- ServletContextAttributeEvent
- 该类表示上下文属性事件,当应用上下文的属性发生变化,如增加、删除、覆盖上下文中的属性时,将触发该事件
- ServletRequestEvent
- 该类表示请求事件,当请求对象发生改变,如创建或销毁请求对象时,触发请求事件
- ServletRequestAttributeEvent
- 该类表示请求属性事件,当请求中的属性发生改变,如增加、删除、覆盖请求中的属性时,触发请求属性事件
- HttpSessionEvent
- 该类表示会话事件,当会话对象发生改变,如创建或销毁会话对象,活化或钝化会话对象时,将触发会话事件
- HttpSessionBindingEvent
- 该类表示会话绑定事件,当会话中的属性发生变化时,如增加、删除、覆盖会话中的属性时,将触发会话绑定事件
2、监听器
每一个事件类型都有一个接口去监听,这就是监听器。就像我派狗仔A负责绯闻部分,狗仔B负责活动进展部分。
Servlet API中定义了8种监听器接口,用来监听不同的事件类型:
- ServletContextListener
- 上下文监听器,监听ServletContextEvent事件
- ServletContextAttributeListener
- 上下文属性监听器,用来监听ServletContextAttribute事件
- ServletRequestListener
- 请求监听器,监听ServletRequestEvent事件
- ServletRequestAttributeListener
- 请求属性监听器,用来监听ServletRequestAttributeEvent事件
- HttpSessionListener
- 会话监听器,监听HttpSessionEvent事件
- HttpSessionActivationListener
- 会话活化监听器,监听HttpSessionEvent事件
- HttpSessionAttributeListener
- 会话属性监听器,监听HttpSessionAttributeEvent事件
- HttpSessionBindingListener
- 会话绑定监听器,监听HttpSessionAttributeEvent事件
在会话相关的事件类型和监听器中,都提到了两个字眼:“活化” 和 “钝化”。这是容器为了提高资源的使用,对会话的一种机制,而且不同的容器(服务器)对它的实现是不一样的,所以比较难以直接测试进行复现。
所谓钝化,就是会话对象还没有超时,但是确实已经活动得比较少了,那么这时就把它从内存放到外存上;当后来它又被使用了,那么又将其取回来,这个叫活化。简单地说,从内到外叫钝化;从外到内叫活化。
3、事件和监听的关系
事件对象是容器创建的,触发的条件也已经定义好。例如,只要容器关闭或启动,就会触发ServletContextEvent事件,容器会创建该类型的对象。
当事件发生后,容器会寻找监听器来处理该事件。监听器需要程序员自定义并配置,然而监听器的类型已经定义好了。例如,当发生了ServletContextEvent事件后,只能使用ServletContextListener监听器来监听。
而我们要做的事,就是要把监听器中的方法进行实现,用来做我们想做的事情。
4、示例:用监听器修改登录计数器
我们有一个计数器,存储在上下文对象中,每次用户登录时就将该计数器数字增加,但是当容器重启,或应用重新部署后,上下文对象将重新创建,所以计数又从1开始,这显然出现了数据丢失。
我们现在的目标,就是利用文本文件对数据进行存储:
- 当容器关闭时,把当前数值保存到文本文件中
- 当容器启动时,从文本文件中读取数值。
显然,我们可以用监听器来监听容器的启动和关闭情况:
4.1 写一个监听器
自定义一个监听器类,并实现ServletContextListener的监听接口,实现其启动和销毁时要执行的方法:
public class VisitCountsListener implements ServletContextListener {
/**
* 容器启动时
* @param sce
*/
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
String path = context.getRealPath("/WEB-INF/temp/visit.txt");
File file = new File(path);
int count = 0;
try {
if (file.exists()) {
BufferedReader reader = new BufferedReader(new FileReader(path));
count = Integer.valueOf(reader.readLine());
}
} catch (IOException e) {
e.printStackTrace();
}
context.setAttribute("count", count);
}
/**
* 容器关闭时
* @param sce
*/
public void contextDestroyed(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
String path = context.getRealPath("/WEB-INF/temp/visit.txt");
File file = new File(path);
try {
if (file.exists()) {
file.delete();
}
file.createNewFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write(context.getAttribute("count").toString());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
x
1
public class VisitCountsListener implements ServletContextListener {
2
3
/**
4
* 容器启动时
5
* @param sce
6
*/
7
public void contextInitialized(ServletContextEvent sce) {
8
ServletContext context = sce.getServletContext();
9
String path = context.getRealPath("/WEB-INF/temp/visit.txt");
10
File file = new File(path);
11
int count = 0;
12
13
try {
14
if (file.exists()) {
15
BufferedReader reader = new BufferedReader(new FileReader(path));
16
count = Integer.valueOf(reader.readLine());
17
}
18
} catch (IOException e) {
19
e.printStackTrace();
20
}
21
22
context.setAttribute("count", count);
23
}
24
25
/**
26
* 容器关闭时
27
* @param sce
28
*/
29
public void contextDestroyed(ServletContextEvent sce) {
30
ServletContext context = sce.getServletContext();
31
String path = context.getRealPath("/WEB-INF/temp/visit.txt");
32
File file = new File(path);
33
34
try {
35
if (file.exists()) {
36
file.delete();
37
}
38
file.createNewFile();
39
40
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
41
writer.write(context.getAttribute("count").toString());
42
writer.flush();
43
writer.close();
44
} catch (IOException e) {
45
e.printStackTrace();
46
}
47
}
48
}
4.2 在web.xml中配置监听器
<listener>
<listener-class>com.zker.VisitCountsListener</listener-class>
</listener>
1
<listener>
2
<listener-class>com.zker.VisitCountsListener</listener-class>
3
</listener>