java中事件监听模式理解

2020-07-01  本文已影响0人  FuriousPws002

引言

最近对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会去调用事件传播器ApplicationEventMulticastermulticastEvent的方法,此处使用的事件传播器为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并不关心会去执行什么逻辑,只有对该事件感兴趣的监听器,自然会去执行具体的逻辑,使代码高度解耦,这样的设计无不让人佩服。

上一篇 下一篇

猜你喜欢

热点阅读