Listener

Listener(监听器) 是一个实现特定接口的 java 程序,用于监听另一个 java 对象的方法调用或者属性改变。

自定义监听器


事件源:Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Person {
private String name;
private int weight;
private PersonListener listener;
// 注册监听器
public void addPersonListener(PersonListener listener) {
this.listener = listener;
}

public void eat() {
weight += 5;
if (listener != null) {
// 监听器存在,将该事件以及与事件有关的参数传入监听器的监听方法
PersonEvent event = new PersonEvent(this);
listener.personEating(event);
}
}
}

监听内容

1
2
3
public interface PersonListener {
public void personEating(PersonEvent event); // 监听方法 需要一个事件对象作为参数
}

事件类

1
2
3
public class PersonEvent {
private Object source;// 事件源
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testName() {
Person p = new Person();
p.setName("小明");
p.setWeight(100);

p.addPersonListener(new PersonListener() {
@Override
public void personEating(PersonEvent event) {
System.out.println("有人在吃饭");
Person p = (Person) event.getSource();
System.out.println(p.getName());
System.out.println(p.getWeight());
}
});

p.eat();
}

Servlet 监听器


Servlet 定义了多种类型的监听器,监听的事件源是3个域对象 ServletContextHttpSessionServletRequest

Servlet规范针对3个域对象的操作,又把监听器划分为三类:

  1. 监听3个域对象的创建和销毁的事件监听器
  2. 监听3个域对象的属性的增加和删除的监听器
  3. 监听绑定到 httpSession 域中的某个对象的状态的监听器

编写 Servlet 监听器,

  • 需要实现一个特定的接口,并针对相应动作覆盖接口中的响应方法。

  • 和其他事件监听器不同的是,Servlet监听器的注册不是直接注册在事件源,而是由 web 容器负责,我们只需要在 web.xml 中使用 <listener> 标签配置好监听器, web 容器就会自动把监听器注册到事件源中

  • web.xml 文件可以配置多个 Servlet 事件监听器,web服务器按照它们在 web.xml 中注册的顺序来加载 Servlet 监听器:

1
2
3
4
5
<!-- 对监听器进行注册 -->
<!-- 监听器和Servlet、Filter不同,不需要url配置,监听器执行不是由用户访问的,监听器 是由事件源自动调用的 -->
<listener>
<listener-class>cn.itcast.servlet.listener.MyServletContextListener</listener-class>
</listener>

ServletContextListener


用于坚定 ServletContext 对象的创建和销毁事件

ServletContext 的作用

  • 保存全局应用数据对象
  • 加载框架配置文件
    • spring 框架
  • 实现任务调度(定时器),启动定时程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package cn.itcast.servlet.listener;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
* 自定义 Context监听器
*
* @author seawind
*
*/
public class MyServletContextListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("监听ServletContext对象销毁了...");
}

@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("监听ServletContext对象创建了...");
// 获得事件源
ServletContext servletContext = sce.getServletContext();
// 向ServletContext 中保存数据

// 启动定时器
final Timer timer = new Timer();
// 启动定时任务

// timer.schedule(new TimerTask() {
// @Override
// // 这就是一个线程
// public void run() {
// System.out.println("定时器执行了...");
// }
// }, 0, 3000); // 马上启动 每隔3秒重复执行

// 指定时间启动定时器
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
Date first = dateFormat.parse("2012-08-07 10:42:00");
timer.schedule(new TimerTask() {
int i;

@Override
public void run() {
i++;
System.out.println("从10点40分开始启动程序,每隔3秒重复执行");
if (i == 10) {
timer.cancel();// 取消定时器任务
}
}
}, first, 3000);
} catch (ParseException e) {
e.printStackTrace();
}
}

}
```

## HttpSessionListener

---

监听 HttpSession 的创建和销毁

```java
package cn.itcast.servlet.listener;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MyHttpSessionListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent se) {
// 通过事件对象获得session 的id
System.out.println("session被创建了");
HttpSession session = se.getSession();
System.out.println("id:" + session.getId());
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("session被销毁了");
HttpSession session = se.getSession();
System.out.println("id:" + session.getId());
}

}

统计在线人数


首先,初始化在线人数、根据前文,ServletContextListener 可以监听 ServletContext 对象的创建,新建一个实现监听器接口的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.itcast.servlet.listener.demo2;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class OnlineCountServletContextListener implements
ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
}

@Override
public void contextInitialized(ServletContextEvent sce) {
// 初始化在线人数为0
ServletContext context = sce.getServletContext();
context.setAttribute("onlinenum", 0);
}
}

利用 HttpSessionListenner 监听 HttpSession 对象的创建和销毁,可统计在线人数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package cn.itcast.servlet.listener.demo2;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class OnlineCountHttpSessionListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent se) {
// 当Session对象被创建时,在线人数 +1
HttpSession session = se.getSession();
ServletContext context = session.getServletContext();

int onlinenum = (Integer) context.getAttribute("onlinenum");
context.setAttribute("onlinenum", onlinenum + 1);

System.out.println(session.getId() + "被创建了...");
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 当Session对象被销毁时,在线人数 - 1
HttpSession session = se.getSession();
ServletContext context = session.getServletContext();

int onlinenum = (Integer) context.getAttribute("onlinenum");
context.setAttribute("onlinenum", onlinenum - 1);

System.out.println(session.getId() + "被销毁了 ...");
}
}

xml 中需要配置

1
2
3
4
5
6
<listener>
<listener-class>cn.itcast.servlet.listener.demo2.OnlineCountServletContextListener</listener-class>
</listener>
<listener>
<listener-class>cn.itcast.servlet.listener.demo2.OnlineCountHttpSessionListener</listener-class>
</listener>

jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>显示在线人数</h1>
${applicationScope.onlinenum }
</body>
</html>

案例:统计在线人数


SannerServletContextListener 干了两件事:初始化List,启动定时器,每隔20秒执行一次

注意:

  1. 因为要从 List 中删除元素,循环用 Iterator 不用 foreach
  2. 在删除 Session 对象时,利用同步锁 解决 保证 Session 的 List 集合长度不能改变。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package cn.itcast.servlet.listener.demo3;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;

public class ScannerServletContextListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
}

@Override
public void contextInitialized(ServletContextEvent sce) {
// 第一件事,创建Session的List集合
final List<HttpSession> sessionList = new ArrayList<HttpSession>();
// 将集合保存ServletContext对象
ServletContext servletContext = sce.getServletContext();
servletContext.setAttribute("sessionList", sessionList);

// 第二件事,启动定时器,每隔20秒执行一次
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("定时session扫描器执行了....");
// 扫描Session的List集合,看哪个Session已经1分钟没用了
// 发现Session1分钟没有使用,销毁Session 从集合移除
synchronized (sessionList) {
Iterator<HttpSession> iterator = sessionList.iterator();
while (iterator.hasNext()) {
HttpSession session = iterator.next();
if (System.currentTimeMillis()
- session.getLastAccessedTime() > 1000 * 60) {
System.out.println(session.getId()
+ "对象已经1分钟没有使用,被销毁了...");
// 销毁Session
session.invalidate();
// 从集合移除Session
iterator.remove();
}
}
}
}
}, 0, 20000);
}
}

ScannerHttpSessionListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package cn.itcast.servlet.listener.demo3;

import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class ScannerHttpSessionListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent se) {
// 在创建Session对象时,将Session对象加入集合
HttpSession httpSession = se.getSession();
ServletContext context = httpSession.getServletContext();

List<HttpSession> sessionList = (List<HttpSession>) context
.getAttribute("sessionList");
synchronized (sessionList) {
sessionList.add(httpSession);
}

// 还是否需要context.setAttribute? ---- 不需要:因为之前得到的是List的地址
context.setAttribute("sessionList", sessionList); // 这行代码写不写无所谓

System.out.println(httpSession.getId() + "被创建了...");
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
}

}

web.xml

1
2
3
4
5
6
<listener>
<listener-class>cn.itcast.servlet.listener.demo3.ScannerServletContextListener</listener-class>
</listener>
<listener>
<listener-class>cn.itcast.servlet.listener.demo3.ScannerHttpSessionListener</listener-class>
</listener>

监听三个域对象属性变化事件的监听器


这三个监听器接口分别是:

  1. ServletContextAttributeListener
  2. HttpSessionAttributeListener
  3. ServletRequestAttributeListener

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
// 向Session数据范围 保存名称为"name",值为"张三"的属性(Attribute)
session.setAttribute("name","张三"); // 触发attributeAdd

// 将session中属性(Attribute)名为"name"的值替换为"李四"
session.setAttribute("name","李四"); // 触发attributeReplaced

// 移除名为"name"的属性
session.removeAttribute("name");// 触发attributeRemoved
%>
</body>
</html>==

MyHttpSessionAttributeListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 package cn.itcast.servlet.listener;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class MyHttpSessionAttributeListener implements
HttpSessionAttributeListener {

@Override
public void attributeAdded(HttpSessionBindingEvent se) {
// 属性添加
System.out.println("向session添加了一个属性...");
HttpSession session = se.getSession();

System.out.println("属性名称:" + se.getName());
System.out.println("属性值:" + session.getAttribute(se.getName())); // se.getValue :这个方法不会返回当前属性(Attribute)的值
}

@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
// 属性移除
System.out.println("从session移除了一个属性....");

System.out.println("属性名称:" + se.getName());

}

@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
// 属性替换
System.out.println("将session中一个属性值替换为其他值...");

HttpSession session = se.getSession();
System.out.println("属性名称:" + se.getName());
System.out.println("属性值:" + session.getAttribute(se.getName()));
}
}

HttpSessionBindingListener接口


实现了 HttpSessionBindingListener 接口的 JavaBean 对象可以感知到自己被绑定到 Session 中和从 Session 中删除的事件。

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package cn.itcast.domain;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

/**
* 使Bean1对象感知 自我被绑定Session中,感知自我被Session解除绑定
*
* @author seawind
*
*/
public class Bean1 implements HttpSessionBindingListener {
private int id;
private String name;

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("Bean1对象被绑定了...");
// 当前对象,操作对象
System.out.println("绑定对象name:" + this.name);
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
System.out.println("Bean1对象被解除绑定了...");
System.out.println("解除绑定对象name:" + this.name);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="cn.itcast.domain.Bean1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
Bean1 bean_Susan = new Bean1();
bean_Susan.setId(100);
bean_Susan.setName("Susan");

// 将bean_Susan对象以“bean1”为名,绑定到Session中
session.setAttribute("bean1",bean_Susan);

Bean1 bean_Mary = new Bean1();
bean_Mary.setId(200);
bean_Mary.setName("Mary");

// 将bean_Susan对象以“bean1”为名,绑定到Session中
session.setAttribute("bean1",bean_Mary);
%>
${bean1.name }
</body>
</html>

注意陷阱:当 Session 绑定的 JavaBean 对象替换时,会让新对象绑定,旧对象解绑

HttpSessionActivationListener接口


使用场景:Session保存数据,很长一段时间没用,但是不能销毁Session对象,又不想占用服务器内存资源 —– 钝化(将服务器内存中数据序列化硬盘上)

  • 实现了HttpSessionActivationListener接口的 JavaBean 对象可以感知自己被活化和钝化的事件
    • 当绑定到 HttpSession 对象中的 JavaBean 对象将要随 HttpSession 对象被钝化之前,web 服务器调用该 JavaBean 对象的 void sessionWillPassivate(HttpSessionBindingEvent event) 方法
    • 当绑定到 HttpSession 对象中的 JavaBean 对象将要随 HttpSession 对象被活化之后,web 服务器调用该 JavaBean 对象的 void sessionDidActive(HttpSessionBindingEvent event) 方法

钝化和活化应该由 Tomcat 服务器 自动进行,所以应该配置 Tomcat :

1
2
3
4
5
6
7
<Context>
<!-- 1分钟不用 进行钝化 表示1分钟 -->
<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
<!-- 钝化后文件存储位置 directory="it315" 存放到it315目录-->
<Store className="org.apache.catalina.session.FileStore" directory="it315"/>
</Manager>
</Context>

demo:

write.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="cn.itcast.domain.Bean2"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 将javabean 对象保存Session中 -->
<%
Bean2 bean2 = new Bean2();
bean2.setName("联想笔记本");
bean2.setPrice(5000);

session.setAttribute("bean2",bean2);
%>
</body>
</html>

read.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 读取javabean对象的数据 -->
读取bean2的数据: ${bean2.name } , ${bean2.price }
</body>
</html>

Bean2.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package cn.itcast.domain;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;

/**
* 感知钝化和活化
*
* @author seawind
*
*/
public class Bean2 implements HttpSessionActivationListener {
private String name;
private double price;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("bean2对象被活化...");
}

@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("bean2对象被钝化...");
}

}
  • 配置Tomcat后,开启服务器,打开write.jsp,等待一分钟,在这一分钟内,打开read.jsp是可以读取到bean2对象的。一分钟过后,read.jsp读取不到bean2对象了,同时控制台输出:

输出 bean2对象被钝化

  • 这时候就实现了钝化的效果。好,接下来按照步骤走,钝化后it315目录在哪里? 在项目文件夹是找不到这个目录的,得去Tomcat服务器的目录去找:tomcat/work/Catalina/localhost/项目工程名/

  • 在it315目录中的确可以看到一个XXXXXX.session的文件,其中XXXXXX就是SessionId,打开这个文件,却没有发现 JavaBean 的任何信息,这是为什么呢?

  • 回顾JavaSE的知识,可以发现 JavaBean 没有序列化。Java对象如果想被序列化,必须实现Serializable接口 —- Bean2 实现该接口:

1
2
3
4
5
import java.io.Serializable;
...
public class Bean2 implements HttpSessionActivationListener, Serializable {
...

接下来,重启服务器,打开write.jsp,一分钟后,控制台照旧输出bean2对象被钝化的消息,再进去XXXXXX.session文件中,发现可以找到 JavaBean 对象的相关信息(虽然是乱码)。接下来再打开read.jsp,发现可以读取到 JavaBean 对象了,并且此时控制台输出:bean2对象被活化...

案例:在线用户列表和踢人功能


demo

MyServletContextListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package cn.itcast.listener;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;

import cn.itcast.domain.User;

/**
* 完成全局数据对象初始化
*
* @author seawind
*
*/
public class MyServletContextListener implements ServletContextListener {

@Override
public void contextDestroyed(ServletContextEvent sce) {
}

@Override
public void contextInitialized(ServletContextEvent sce) {
// 所有在线用户数据集合
Map<User, HttpSession> map = new HashMap<User, HttpSession>();
// 将集合保存ServletContext 数据范围
ServletContext servletContext = sce.getServletContext();

servletContext.setAttribute("map", map);
}
}

JavaBean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package cn.itcast.domain;

import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

/**
* User对象自我感知,绑定Session和解除绑定
*/
public class User implements HttpSessionBindingListener {
private int id;
private String username;
private String password;
private String role;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getRole() {
return role;
}

public void setRole(String role) {
this.role = role;
}

@Override
public void valueBound(HttpSessionBindingEvent event) {
// 将新建立Session 和 用户 保存ServletContext 的Map中
HttpSession session = event.getSession();
ServletContext servletContext = session.getServletContext();

Map<User, HttpSession> map = (Map<User, HttpSession>) servletContext
.getAttribute("map");

// 将新用户加入map
map.put(this, session);
}

@Override
public void valueUnbound(HttpSessionBindingEvent event) {
// 根据user对象,从Map中移除Session
HttpSession session = event.getSession();
ServletContext servletContext = session.getServletContext();

Map<User, HttpSession> map = (Map<User, HttpSession>) servletContext
.getAttribute("map");

// 从map移除
map.remove(this);
}
}

LoginServlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package cn.itcast.servlet;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import cn.itcast.domain.User;
import cn.itcast.utils.JDBCUtils;

/**
* 登陆
*
* @author seawind
*
*/
public class LoginServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");

String username = request.getParameter("username");
String password = request.getParameter("password");

String sql = "select * from user where username = ? and password = ?";
Object[] args = { username, password };

QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
try {
User user = queryRunner.query(sql,
new BeanHandler<User>(User.class), args);
// 判断登陆是否成功
if (user == null) {
// 失败
request.setAttribute("msg", "用户名或者密码错误!");
request.getRequestDispatcher("/login.jsp").forward(request,
response);
} else {
// 成功
request.getSession().invalidate();// 销毁之前状态

// 先判断该用户是否已经登陆,如果已经登陆,将Session销毁
Map<User, HttpSession> map = (Map<User, HttpSession>) getServletContext()
.getAttribute("map");
for (User hasLoginUser : map.keySet()) {
if (hasLoginUser.getUsername().equals(user.getUsername())) {
// 此用户之前登陆过 --- 消灭Session
HttpSession hasLoginSession = map.get(hasLoginUser);
hasLoginSession.invalidate();// session 被摧毁,移除所有对象
// 若不使用break,则会调用map的remove方法(invalidate -> valueUnbound),发生并发异常
break;
}
}

request.getSession().setAttribute("user", user); // 将user对象绑定到Session,触发valueBound
response.sendRedirect("/day18kick/list.jsp");
}
} catch (SQLException e) {
e.printStackTrace();
}
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}

KickServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package cn.itcast.servlet;

import java.io.IOException;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import cn.itcast.domain.User;

/**
* 接收被踢id
*
* @author seawind
*
*/
public class KickServlet extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("id");// 被踢人 id

Map<User, HttpSession> map = (Map<User, HttpSession>) getServletContext()
.getAttribute("map");
// 查找目标id
for (User hasLoginUser : map.keySet()) {
if (hasLoginUser.getId() == Integer.parseInt(id)) {
// 找到被踢用户记录
HttpSession hasLoginSession = map.get(hasLoginUser);
hasLoginSession.invalidate();
break;
}
}
// 跳转回 列表页面
response.sendRedirect("/day18kick/list.jsp");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

list.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>在线用户列表</h1>
<h2>当前用户 ${user.username }</h2>
<!-- 将ServletContext中 map 数据显示出来 -->
<c:forEach items="${map}" var="entry">
<!-- 只有管理员可以踢人 -->
<!-- 管理员不能被踢 -->
${entry.key.username }
<c:if test="${user.role == 'admin' && entry.key.role != 'admin' }">
<a href="/day18kick/kick?id=${entry.key.id}">踢下线</a>
</c:if>
<br/>
</c:forEach>
</body>
</html>

案例:服务器启动创建第一个管理员账号

不要忘记配置 xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebListener
public class MyServletContextListener implements ServletContextListener {
private IUserService service = new UserServiceImpl();
private IUserDAO dao = new UserDAOImpl();
public void contextInitialized(ServletContextEvent ev) {
// 检查数据库中是否存在 root 账号,不存在就创建
User u = dao.checkUsername("root");
if (u == null) {
User user = new User();
user.setName("root");
user.setPassword("admin");
service.save(user);
}
}
}
文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2019/07/18/Listener/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论