事件驱动

作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!

提到事件驱动,熟悉Java的童鞋会想到Java Swing中的事件模型:按钮的按下、释放;鼠标在窗口的移入、移出;输入框被做焦点、失去焦点、输入,都会要求编写一个事件监听器。

事件驱动有几个用途:

  1. 实现模块间的松耦合。
  2. 将一个大的业务场景拆分成几个独立的小部分。
  3. 提供扩展点,实现插件机制的功能。
  4. 用于异步操作场景。

例如,一个用户登陆的业务场景:用户执行登陆操作后,会进行用于登陆成功率的统计日志输出,如果登陆成功需要通知用户在线模块某一个用户成功登陆了。
用事件驱动非常方便解决上述问题:执行登陆操作后,产生一个登陆事件,编写两件事件监听器(一个处理登陆成功率的统计日志输出,一个通知用户在线模块)注册到登陆事件。如果后续在用户执行登陆后还有新的业务需求,只需编写新的事件监听器注册到登陆事件。这样的做法符合开闭原则,实现新的功能不影响已有功能。如果要移除某个功能点,直接移除对应的事件监听器。

如果查找事件驱动的实现机制,网上查到的大部分都是类似于下面的“经典事件驱动模式”的说明。但在实际的业务场景中,这样的实现不能满足需求,后面有一个”改进版事件驱动模式”。当然,需要再进一步改进才能满足实际中的需求,例如:异步执行,线程池,事件与事件监听器的注册配置,运行状态监控等等。

经典事件驱动模式

经典的事件驱动有三个基本元素:

  • 事件源:负责事件监听器的管理,创建并派发事件。
  • 事件监听器:接收并处理事件。
  • 事件。
经典事件驱动模式

Event源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 事件
* @author aofeng <aofengblog@163.com>
*/
public class Event {
private Object data;
public Event(Object obj){
this.data = obj;
}
public Object getData() {
return data;
}
}

EventListener源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 事件监听器(监听一个或多个事件并进行具体的处理)
*
* @author aofeng <aofengblog@163.com>
*/
public interface EventListener {
/**
* 处理事件
*
* @param event 事件
*/
public void execute(Event event);
}

EventSource源代码

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
import java.util.ArrayList;
import java.util.List;
/**
* 事件源(事件发送者)
*
* @author aofeng <aofengblog@163.com>
*/
public class EventSource {
private List<EventListener> listeners = new ArrayList<EventListener>();
public EventSource() {
}
/**
* 添加事件监听器
*
* @param listener 事件监听器
*/
public boolean addListener(EventListener listener) {
return listeners.add(listener);
}
/**
* 移除事件监听器
*
* @param listener 移除事件监听器
*/
public boolean removeListener(EventListener listener) {
return listeners.remove(listener);
}
/**
* 派发事件
*
* @param data 事件
*/
public void fire(Object data) {
for (EventListener listener : listeners) {
listener.execute(new Event(data));
}
}
}

ClientMain源代码

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
/**
* 事件驱动调用示例
* @author aofeng <aofengblog@163.com>
*/
public class ClientMain {
public static void main(String[] args) {
EventSource eventSource = new EventSource();
eventSource.addListener(new HelloWorldListener());
eventSource.addListener(new SimpleListener());
eventSource.fire("hello, world!");
}
public static class HelloWorldListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件附带的数据:" + event.getData());
}
}
public static class SimpleListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件附带的数据:" + event.getData());
}
}
}

执行ClientMain类后的结果类似如下:

监听器:ClientMain$HelloWorldListener@45bab50a接收到事件,事件附带的数据:hello, world!
监听器:ClientMain$SimpleListener@7150bd4d接收到事件,事件附带的数据:hello, world!

改进版事件驱动模式

在经典版的基础上,将EventSource按职责进行拆分,一部分(EventSource)负责事件派分,另一部分(EventManagement)负责事件的管理(添加事件、移除事件、获取指定事件的监听器列表)。并且实现了向不同的事件注册各自的监听器。
改进版事件驱动模式

Event源代码

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
/**
* 事件
*
* @author aofeng <aofengblog@163.com>
*/
public class Event {
// 事件附带的数据
private Object data;
// 事件类型
private String eventType;
public Event(String eventType, Object obj){
this.eventType = eventType;
this.data = obj;
}
public Object getData() {
return this.data;
}
public String getEventType() {
return this.eventType;
}
}

EventListener源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 事件监听器(监听一个或多个事件并进行具体的处理)
*
* @author aofeng <aofengblog@163.com>
*/
public interface EventListener {
/**
* 处理事件
*
* @param event 事件
*/
public void execute(Event event);
}

EventSource源代码

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
import java.util.List;
/**
* 事件源(事件发送者)
*
* @author aofeng <aofengblog@163.com>
*/
public class EventSource {
// 事件管理器
private EventManagement eventManagement;;
public EventSource(EventManagement eventManagement){
this.eventManagement = eventManagement;
}
/**
* 派发事件
*
* @param data 事件
*/
public void fire(Event event) {
if (null == event) {
return;
}
List<EventListener> listeners = eventManagement.getEventListeners(event.getEventType());
if (null == listeners) {
return;
}
for (EventListener listener : listeners) {
listener.execute(event);
}
}
}

EventManagement源代码

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
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 事件管理器。负责事件监听器的注册和移除操作,并提供获取指定事件的监听器列表功能。
*
* @author aofeng <aofengblog@163.com>
*/
public class EventManagement {
private Map<String, List<EventListener>> map = new HashMap<String, List<EventListener>>();
public EventManagement(){
}
/**
* 向指定事件添加一个监听器
*
* @param eventType 事件类型
* @param listener 事件监听器
* @return 添加成功返回true;添加失败返回false
*/
public boolean addListener(String eventType, EventListener listener){
List<EventListener> listeners = map.get(eventType);
if (null == listeners) {
listeners = new ArrayList<EventListener>();
}
boolean result = listeners.add(listener);
map.put(eventType, listeners);
return result;
}
/**
* 移除事件的某一个监听器
*
* @param eventType 事件类型
* @param listener 事件监听器
* @return 移除成功返回true;移除失败返回false
*/
public boolean removeListener(String eventType, EventListener listener){
List<EventListener> listeners = map.get(eventType);
if (null != listeners) {
return listeners.remove(listener);
}
return false;
}
/**
* 获取指定事件的监听器
*
* @param eventType 事件类型
* @return 如果指定的事件没有监听器返回null;否则返回监听器列表
*/
public List<EventListener> getEventListeners(String eventType) {
return map.get(eventType);
}
}

ClientMain源代码

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
/**
* 事件驱动调用示例
* @author aofeng <aofengblog@163.com>
*/
public class ClientMain {
public static void main(String[] args) {
EventManagement eventManagement = new EventManagement();
eventManagement.addListener("read", new HelloWorldListener());
eventManagement.addListener("write", new SimpleListener());
EventSource eventSource = new EventSource(eventManagement);
eventSource.fire(new Event("read", "this is a read event"));
eventSource.fire(new Event("write", "this is a write event"));
}
public static class HelloWorldListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件类型是:"
+ event.getEventType() + ", 事件附带的数据:" + event.getData());
}
}
public static class SimpleListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件类型是:"
+ event.getEventType() + ", 事件附带的数据:" + event.getData());
}
}
}

执行ClientMain类后的结果类似如下:

监听器:ClientMain$HelloWorldListener@64c3c749接收到事件,事件类型是:read, 事件附带的数据:this is a read event
监听器:ClientMain$SimpleListener@6bbc4459接收到事件,事件类型是:write, 事件附带的数据:this is a write event