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

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

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

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

2. ApplicationEvent

2.1 接口清單

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

/*
 * 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)建一個(gè)應(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ā)生時(shí)的系統(tǒng)時(shí)間
     */
    public final long getTimestamp() {
        return this.timestamp;
    }

}

2.2 UML

UML

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

2.3 PayloadApplicationEvent

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

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

default

3. ApplicationListener

3.1 接口清單

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

@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開(kāi)始支持,需要實(shí)現(xiàn)的方法為boolean supportsEventType(Class<? extends ApplicationEvent> eventType);,注意這里推斷的對(duì)象是Class.
  • GenericApplicationListener: 支持事件消費(fèi)排序與事件類型匹配,Spring4.2開(kāi)始支持,需要實(shí)現(xiàn)的方法為boolean supportsEventType(ResolvableType eventType);,注意這里推斷的對(duì)象是ResolvableType.

3.3 注解支持-@EventListener

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

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

4.ApplicationEventPublisher&ApplicationEventMulticaster

4.1 ApplicationEventPublisher

4.1.1 接口清單

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

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

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

}
4.1.2 UML
UML

這里再次驗(yàn)證,容器即為IOC的ApplicationEventPublisher.

4.2 ApplicationEventMulticaster

Spring中的publisher只提供了發(fā)布事件的接口,然而一個(gè)事件監(jiān)聽(tīng)器模式少不了注冊(cè)監(jiān)聽(tīng)器這件事情,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)聽(tīng)器、移除監(jiān)聽(tīng)器、發(fā)布事件。

4.2.2 UML
UML
  • AbstractApplicationEventMulticaster: 內(nèi)部持有一個(gè)defaultRetriever成員變量,該變量為ListenerRetriever,其內(nèi)部使用Set存儲(chǔ)ApplicationListener。 AbstractApplicationEventMulticaster添加監(jiān)聽(tīng)器的操作為this.defaultRetriever.applicationListeners.add(listener);.有興趣的讀者可以自行擴(kuò)展閱讀.
  • SimpleApplicationEventMulticaster: 繼承自AbstractApplicationEventMulticaster,內(nèi)置private Executor taskExecutor;-任務(wù)執(zhí)行器。默認(rèn)情況下,監(jiān)聽(tīng)器都以同步的方式進(jìn)行,但是會(huì)由于個(gè)別監(jiān)聽(tīng)器速度過(guò)慢,導(dǎo)致任務(wù)進(jìn)度阻塞,因此該事件發(fā)布器也支持了以線程池來(lái)提交異步任務(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 為什么要同時(shí)定義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 {
            // 如果事件對(duì)象不是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 {
            // 重點(diǎn),可以看到publishEvent其實(shí)是通過(guò)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);
            }
        }
    }

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

總結(jié)

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

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

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