[Spring]詳解Spring中的事件監(jiān)聽器模式

1. 事件監(jiān)聽器模式的重要因素

  • Event Object: 事件,事件源會將事件進(jìn)行發(fā)布。Spring中的事件對象為ApplicationEvent.
  • Event Listener: 事件監(jiān)聽器,負(fù)責(zé)處理訂閱的事件. Spring中對應(yīng)的事件監(jiān)聽器接口為ApplicationListener.
  • Event Source: 事件源,負(fù)責(zé)發(fā)布事件并通知事件監(jiān)聽器。Spring中對應(yīng)的事件源接口為ApplicationEventPublisher.

關(guān)于事件監(jiān)聽器模式,如果你不夠熟悉,可以在我的上篇博客得到解答->點我前往

2. ApplicationEvent

2.1 接口清單

值得一提的是,Spring中的ApplicationEvent是內(nèi)置事件源的,這意味著在監(jiān)聽器中可以獲取事件源,而Spring中的事件源通常為容器本身.
同時,事件發(fā)生的同時,會記錄當(dāng)前系統(tǒng)時間戳

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context;

import java.util.EventObject;

/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public abstract class ApplicationEvent extends EventObject {

    /** use serialVersionUID from Spring 1.2 for interoperability. */
    private static final long serialVersionUID = 7099057708183571937L;

    /** System time when the event happened. */
    private final long timestamp;


    /**
     * Create a new ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})<br>
     * 創(chuàng)建一個應(yīng)用事件,其中source為事件源,并且不能為空.
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }


    /**
     * Return the system time in milliseconds when the event happened.<br>
     * 返回當(dāng)前事件發(fā)生時的系統(tǒng)時間
     */
    public final long getTimestamp() {
        return this.timestamp;
    }

}

2.2 UML

UML

看類圖可以知道,ApplicationEvent繼承自JDK的EventObject,同時還有一個抽象子類ApplicationContextEvent,為什么要再抽象出這個子類呢,因為Object本身的含義太過廣泛,Spring為了定義容器事件,強(qiáng)制約束source對象本身的類型為ApplicationContext.
最后從這個抽象類,衍生了一系列單一職責(zé)的事件。分別對應(yīng)容器的關(guān)閉、刷新、啟動、停止等階段.

2.3 PayloadApplicationEvent

Spring 4.2 后推出一個一個基于泛型對事件進(jìn)行包裝的類,在此之前,發(fā)布的Event都必須繼承自ApplicationEvent.加入Payload機(jī)制后,在發(fā)布事件的時候,可以傳輸任意的Object,Spring內(nèi)部都會用PayloadApplicationEvent對事件進(jìn)行包裝.

具體的改進(jìn)在org.springframework.context.ApplicationEventPublisher#publishEvent(java.lang.Object)可以做更加深入的了解。

default

3. ApplicationListener

3.1 接口清單

函數(shù)式接口,同時監(jiān)聽的事件需為繼承自ApplicationEvent的類,如果Spring為4.2后的,可以不受這個限制,因為內(nèi)部使用PayloadApplicationEvent進(jìn)行了包裝,在事件源發(fā)布事件時,會觸發(fā)onApplicationEvent通知監(jiān)聽器。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     * 處理應(yīng)用事件
     */
    void onApplicationEvent(E event);

}

3.2 UML

UML
  • SmartApplicationListener: 支持事件消費(fèi)排序與事件類型匹配,Spring3.0開始支持,需要實現(xiàn)的方法為boolean supportsEventType(Class<? extends ApplicationEvent> eventType);,注意這里推斷的對象是Class.
  • GenericApplicationListener: 支持事件消費(fèi)排序與事件類型匹配,Spring4.2開始支持,需要實現(xiàn)的方法為boolean supportsEventType(ResolvableType eventType);,注意這里推斷的對象是ResolvableType.

3.3 注解支持-@EventListener

Spring4.2后,對事件監(jiān)聽器增加了注解的支持,無需實現(xiàn)接口,只需要通過@EventListener來直接在需要響應(yīng)的方法上標(biāo)注即可,配合Async@Order注解還可以支持異步與消費(fèi)順序聲明.

注意,注解標(biāo)注的方法上,最好聲明為void,如果返回類型為數(shù)組或者集合,Spring會將每個元素作為新的事件進(jìn)行發(fā)布。

4.ApplicationEventPublisher&ApplicationEventMulticaster

4.1 ApplicationEventPublisher

4.1.1 接口清單

通常在SpringIOC中,容器本身會作為ApplicationEventPublisher去進(jìn)行事件的發(fā)布.
同時,開發(fā)者還可以通過Aware接口訪問到ApplicationEventPublisher實例.來發(fā)布自定義事件.

@FunctionalInterface
public interface ApplicationEventPublisher {
    // 通知所有與此應(yīng)用程序注冊的匹配偵聽器一個應(yīng)用程序事件。
    // 事件可以是框架事件(例如ContextRefreshedEvent)或特定于應(yīng)用程序的事件。
    default void publishEvent(ApplicationEvent event) {
        publishEvent((Object) event);
    }

    // 通知所有與此應(yīng)用程序注冊的匹配偵聽器事件。
    // 如果指定的事件不是ApplicationEvent,則將其包裝在PayloadApplicationEvent中。
    void publishEvent(Object event);

}
4.1.2 UML
UML

這里再次驗證,容器即為IOC的ApplicationEventPublisher.

4.2 ApplicationEventMulticaster

Spring中的publisher只提供了發(fā)布事件的接口,然而一個事件監(jiān)聽器模式少不了注冊監(jiān)聽器這件事情,ApplicationEventMulticaster就是為了解決這件事而產(chǎn)生的。

4.2.1 接口清單
public interface ApplicationEventMulticaster {

    void addApplicationListener(ApplicationListener<?> listener);

    void addApplicationListenerBean(String listenerBeanName);

    void removeApplicationListener(ApplicationListener<?> listener);

    void removeApplicationListenerBean(String listenerBeanName);

    void removeAllListeners();

    void multicastEvent(ApplicationEvent event);

    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);

}

從接口清單我們可以看到,ApplicationEventMulticaster支持添加監(jiān)聽器、移除監(jiān)聽器、發(fā)布事件。

4.2.2 UML
UML
  • AbstractApplicationEventMulticaster: 內(nèi)部持有一個defaultRetriever成員變量,該變量為ListenerRetriever,其內(nèi)部使用Set存儲ApplicationListener。 AbstractApplicationEventMulticaster添加監(jiān)聽器的操作為this.defaultRetriever.applicationListeners.add(listener);.有興趣的讀者可以自行擴(kuò)展閱讀.
  • SimpleApplicationEventMulticaster: 繼承自AbstractApplicationEventMulticaster,內(nèi)置private Executor taskExecutor;-任務(wù)執(zhí)行器。默認(rèn)情況下,監(jiān)聽器都以同步的方式進(jìn)行,但是會由于個別監(jiān)聽器速度過慢,導(dǎo)致任務(wù)進(jìn)度阻塞,因此該事件發(fā)布器也支持了以線程池來提交異步任務(wù)的方式消費(fèi)事件.
    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)) {
            // 如果線程池不為空,則使用線程池提交任務(wù).
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

4.3 為什么要同時定義ApplicationEventMulticaster和ApplicationEventPublisher?

org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)中,我們可以看到,容器內(nèi)部是怎么發(fā)送事件的.

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");

        // Decorate event as an ApplicationEvent if necessary
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent) event;
        }
        else {
            // 如果事件對象不是ApplicationEvent,則使用PayloadApplicationEvent進(jìn)行包裝
            applicationEvent = new PayloadApplicationEvent<>(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
            }
        }

        // Multicast right now if possible - or lazily once the multicaster is initialized
        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        }
        else {
            // 重點,可以看到publishEvent其實是通過ApplicationEventMulticaster進(jìn)行真正的事件發(fā)布
            getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        }

        // Publish event via parent context as well...
        // 一些遞歸的操作
        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
            }
            else {
                this.parent.publishEvent(event);
            }
        }
    }

簡單地說,就是ApplicationEventPublisher是一個簡單的事件發(fā)布接口,只負(fù)責(zé)聲明入口,而ApplicationEventMulticaster負(fù)責(zé)處理真正的事件發(fā)布邏輯。這其實是一種委托的思想.你可以在Spring隨處發(fā)現(xiàn)這種現(xiàn)象,如Registry接口其實真正的執(zhí)行者為DefaultListableBeanFactory.

總結(jié)

  • Spring中的事件監(jiān)聽器模式擴(kuò)展自JDK中的事件監(jiān)聽器模型。
  • 事件在早期版本必須繼承自ApplicationEvent,4.2后Spring引入了PayloadApplicationEvent進(jìn)行兼容,支持以O(shè)bject的形式發(fā)送事件.
  • 事件源在IOC中通常為容器本身.
  • 事件監(jiān)聽器支持實現(xiàn)接口和注解形式進(jìn)行實現(xiàn),同時可以使用@Order注解來指定消費(fèi)順序。
  • 避免在事件監(jiān)聽器中調(diào)用事件源的發(fā)布事件,引起循環(huán)引用的問題。
  • Spring以委托的思想,建立了事件發(fā)布者的接口視圖->ApplicationEventPublisher,其中真正的執(zhí)行者則為ApplicationEventMulticaster接口的實現(xiàn)類.
  • ApplicationEventMulticaster提供了異步的支持。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容