引言
最近对java中事件,监听模式有点兴趣,按照自己的理解写了一些例子,若有不正,望读者斧正。
什么是事件监听模式
事件监听模式类似于设计模式中的观察者模式,最核心的概念有如下三个
- 事件
- 监听器
- 事件源
上面三个概念,为了便于理解,我用一句话来类比,聋子看到火车开灯就跳舞,对比概念如下:
概念 | 语句 |
---|---|
事件 | 开灯 |
监听器 | 聋子 |
事件源 | 火车 |
其中事件一般是由事件源发起的某个动作,所以事件中理所当然包含了事件源本身,以便知晓是哪个事件发起的。而监听器可以理解为当事件源触发某个事件时,监听器对该事件有兴趣,就能够做出相应的响应,这个响应就是执行具体的业务逻辑。事件源中包含了监听器集合,以便触发事件时,通知监听器。
java中的事件监听概念
jdk在java.util包中为我们提供了事件EventObject
父类,以及监听器EventListener
接口。
EventObject
类中source变量就是事件源
/**
* The object on which the Event initially occurred.
*/
protected transient Object source;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @exception IllegalArgumentException if source is null.
*/
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
例子说明
例子围绕如下三句话来展开
- 瞎子听到火车鸣笛就唱歌,瞎子对火车鸣笛感兴趣,感兴趣导致的结果就是唱歌。
- 聋子看到火车开灯就跳舞,聋子对火车开灯感兴趣,感兴趣导致的结果就是跳舞。
- 聋子看到飞机扔炸弹就使劲跑,聋子对飞机扔炸弹感兴趣,感兴趣导致的结果就是使劲跑。
定义事件源抽象类AbstractEventSource
,该类主要包含了监听器集合,以及提供方法注册监听器到事件源
/**
* 事件源抽象类,包含监听器集合
*/
public abstract class AbstractEventSource {
@Getter
private final Collection<EventListener> listeners = new HashSet<>();
public void register(EventListener listener) {
Objects.requireNonNull(listener, "The listener parameter cannot be null");
this.listeners.add(listener);
}
public void register(Collection<EventListener> listeners) {
Objects.requireNonNull(listeners, "The listeners parameter cannot be null");
this.listeners.addAll(listeners);
}
}
定义火车事件源Train
/**
* 瞎子听到火车鸣笛就唱歌
* 聋子看到火车开灯就跳舞
*/
public class Train extends AbstractEventSource {
private static final int LISTENER_TYPE_BLIND = 1;
private static final int LISTENER_TYPE_DEAF = 2;
public void onTrumpet() {
notice(LISTENER_TYPE_BLIND);
}
public void onLight() {
notice(LISTENER_TYPE_DEAF);
}
private void notice(int listenerType) {
for (EventListener listener : getListeners()) {
if (LISTENER_TYPE_BLIND == listenerType && listener instanceof BlindListener) {
((BlindListener) listener).sing(new TrumpetEvent(this));
} else if (LISTENER_TYPE_DEAF == listenerType && listener instanceof DeafListener) {
((DeafListener) listener).dance(new LightEvent(this));
}
}
}
@Override
public String toString() {
return "火车事件源";
}
}
定义飞机事件源Warcraft
/**
* 聋子看到飞机扔炸弹就使劲跑
*/
public class Warcraft extends AbstractEventSource {
public void onBomb() {
notice();
}
private void notice() {
for (EventListener listener : getListeners()) {
if (listener instanceof DeafListener) {
((DeafListener) listener).run(new BombEvent(this));
}
}
}
@Override
public String toString() {
return "飞机事件源";
}
}
定义如下三个事件,扔炸弹BombEvent
,开灯LightEvent
,鸣笛TrumpetEvent
public class BombEvent extends EventObject {
public BombEvent(Warcraft source) {
super(source);
}
@Override
public Warcraft getSource() {
return (Warcraft) super.getSource();
}
@Override
public String toString() {
return "扔炸弹事件";
}
}
public class LightEvent extends EventObject {
public LightEvent(Train source) {
super(source);
}
@Override
public Train getSource() {
return (Train) super.getSource();
}
@Override
public String toString() {
return "开灯事件";
}
}
public class TrumpetEvent extends EventObject {
public TrumpetEvent(Train source) {
super(source);
}
@Override
public Train getSource() {
return (Train) super.getSource();
}
@Override
public String toString() {
return "鸣笛事件";
}
}
定义如下两个监听器瞎子BlindListener
,聋子DeafListener
/**
* 瞎子,监听火车鸣笛
*/
@RequiredArgsConstructor
public class BlindListener implements EventListener {
private final String person;
public void sing(TrumpetEvent event) {
System.err.println(event + "被触发了,事件源为:" + event.getSource());
System.err.println("The " + person + " start sing");
}
}
/**
* 聋子,监听火车开灯、飞机扔炸弹
*/
@RequiredArgsConstructor
public class DeafListener implements EventListener {
private final String person;
public void dance(LightEvent event) {
System.err.println(event + "被触发了,事件源为:" + event.getSource());
System.err.println("The " + person + " start dance");
}
public void run(BombEvent event) {
System.err.println(event + "被触发了,事件源为:" + event.getSource());
System.err.println("The " + person + " start run");
}
}
编写测试类TestEvent
public class TestEvent {
@Test
public void test() {
Train train = new Train();
Warcraft warcraft = new Warcraft();
Set<EventListener> listeners = new HashSet<EventListener>() {{
add(new BlindListener("瞎子A"));
add(new BlindListener("瞎子B"));
add(new DeafListener("聋子A"));
add(new DeafListener("聋子B"));
}};
train.register(listeners);
warcraft.register(listeners);
train.onTrumpet();
System.err.println("====");
train.onLight();
System.err.println("====");
warcraft.onBomb();
}
}
执行测试类后,为如下结果
鸣笛事件被触发了,事件源为:火车事件源
The 瞎子A start sing
鸣笛事件被触发了,事件源为:火车事件源
The 瞎子B start sing
====
开灯事件被触发了,事件源为:火车事件源
The 聋子B start dance
开灯事件被触发了,事件源为:火车事件源
The 聋子A start dance
====
扔炸弹事件被触发了,事件源为:飞机事件源
The 聋子B start run
扔炸弹事件被触发了,事件源为:飞机事件源
The 聋子A start run
从执行的结果可以看出来,当事件源触发某个事件时,对该事件感兴趣的监听器,会去执行监听器的具体逻辑。同时,事件源可以同时注册多个不同类型的监听器,而监听器也可以监听多个不同种类的事件。
spring中的事件监听应用
spring生态中运用了大量的事件,监听模式,其中ApplicationEvent
为所有事件的父类,ApplicationListener
为所有监听器的父类。这里拿事件ContextRefreshedEvent
和监听器ClearCachesApplicationListener
来举例。
我们知道在AbstractApplicationContext
类中有一个很重要的方法就是refresh
,该方法除了包括spring初始化bean的逻辑之外,还有其他处理逻辑,其中就有事件以及监听器的注册,而在refresh
方法底部,有一个名为finishRefresh
的方法,这个方法中触发了一个事件ContextRefreshedEvent
。
代码如下
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
try {
...
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
...
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
而方法publishEvent
会去调用事件传播器ApplicationEventMulticaster
的multicastEvent
的方法,此处使用的事件传播器为SimpleApplicationEventMulticaster
,方法如下
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
当事件为ContextRefreshedEvent
时,getApplicationListeners
获取的监听器集合中有一个名为ClearCachesApplicationListener
的监听器(该监听器在refresh方法中的registerListeners被注册),当执行invokeListener
时,会去执行监听器里面的具体逻辑,代码如下:
public void onApplicationEvent(ContextRefreshedEvent event) {
ReflectionUtils.clearCache();
clearClassLoaderCaches(Thread.currentThread().getContextClassLoader());
}
也就是,随着spring中applciation的refresh完成,事件源ApplicationContext
会触发事件ContextRefreshedEvent
,而监听器ClearCachesApplicationListener
刚刚又对事件ContextRefreshedEvent
感兴趣,所以,会去执行监听器里面的具体逻辑,这里的逻辑就是去清空spring内部的方法和成员变量缓存。从上面分析可以看到,当refresh完成时,只需要去触发事件,该事件通过事件广播器去传播,而refresh并不关心会去执行什么逻辑,只有对该事件感兴趣的监听器,自然会去执行具体的逻辑,使代码高度解耦,这样的设计无不让人佩服。
网友评论