Spring中ApplicationListener -【Spring底層原理】

blog58

1、概述

在前面講到了bean的拓展,這篇文章將講述事件的拓展java

ApplicationListener 是用來監聽容器中發生的事件,只要事件發生,就會觸發監聽器的回調,完成事件驅動模型的開發spring

這裏經過兩個問題來進行概述,什麼是事件?spring是如何實現的?markdown

【1】什麼是事件app

事件是能夠被控件識別的操做,如按下肯定按鈕,選擇某個單選按鈕或者複選框。每一種控件有本身能夠識別的事件,如窗體的加載、單擊、雙擊等事件,編輯框(文本框)的文本改變事,等等。事件有系統事件和用戶事件。系統事件由系統激發,如時間每隔24小時,銀行儲戶的存款日期增長一天。用戶事件由用戶激發,如用戶點擊按鈕,在文本框中顯示特定的文本。事件驅動控件執行某項功能。異步

【2】如何實現源碼分析

Spring對事件機制也提供了支持,一個事件被髮布後,被對應的監聽器監聽到,執行對應方法。而且Spring內已經提供了許多事件,ApplicationEvent能夠說是Spring事件的頂級父類。ApplicationListener 是監聽器的頂級接口,事件被觸發後,onApplicationEvent方法就會執行測試

若是咱們要寫監聽器,就要寫這個監聽器接口的實現類,ApplicationEvent泛型就是咱們要監聽的類,因此咱們要監聽或者是發佈,都是ApplicationEvent及其下面的子事件,經過查看ApplicationEvent類,咱們發現有如下子事件:this

  1. ContextClosedEvent:關閉容器發佈這個事件
  2. ContextRefreshedEvent:容器刷新完成發佈這個事件(全部bean都進行了實例化,完成了建立)
  3. ContextStoppedEvent:容器中止時發佈這個事件
  4. ContextStartedEvent:容器開始執行時發佈這個事件

實現步驟:spa

  1. 寫一個監聽器(ApplicationListener 實現類)來監聽某個事件(ApplicationEvent及其子類)
  2. 把監聽器加到容器中
  3. 只要容器中有相關事件的發佈,咱們就能監聽到這個事件,監聽的即是上面說到的子事件
  4. 自定義發佈一個事件:applicationContext.publishEvent()

2、實例分析

// 啓動測試類
@Test
public void TestMain(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    // 本身發佈一個事件
    applicationContext.publishEvent(new ApplicationEvent("本身發佈的事件") {
    });
    applicationContext.close();
}

// ApplicationListener實現類
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
    // 當容器中發佈此事件後,該方法會觸發
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println("收到的事件:" + applicationEvent);
    }
}

// 配置類
@Configuration
@ComponentScan("listener")
public class AppConfig {
}
複製代碼

運行啓動類,輸出結果以下,如下三點說一下:3d

  • 容器啓動時,會執行容器刷新完成事件,也就是ContextRefreshedEvent
  • 容器關閉時,會執行容器關閉事件,也就是ContextClosedEvent
  • 在啓動類中,經過publishEvent來發布事件,執行這個方法的時候,ApplicationListener 就能監聽到這個事件,就會回調onApplicationEvent執行

image-20210320174351733

3、源碼分析

在上面的案例中,收到了三個事件,分別是:ContextRefreshedEventContextClosedEvent以及本身定義的MainTest$1[source=本身發佈的事件],這幾個事件在底層是如何收到的呢?,咱們就經過源碼來進行分析,在回掉方法onApplicationEvent打上斷點,經過Debug查看源碼:

【1】事件發佈

經過Debug,咱們能夠看到,最早收到ContextRefreshedEvent事件,下面我們就根據方法調用棧分析ContextRefreshedEvent如何發佈的

image-20210320201912672

容器建立對象,調用refresh()方法——>finishRefresh()方法——>publishEvent()方法,調用getApplicationEventMulticaster()方法獲取事件的多播器,也就是派發器

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    Object applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }

    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        // 獲取事件的多播器,也就是派發器
        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }

    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }

}
複製代碼

調用multicastEvent進行事件的派發

  • 獲取全部ApplicationListener進行遍歷
  • 判斷是否能夠用executor異步執行,能夠的話使用executor進行異步派發,派發的時候咱們能夠自定義是同步仍是異步
  • 不然同步執行派發
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    Executor executor = this.getTaskExecutor();
    Iterator var5 = this.getApplicationListeners(event, type).iterator();

    while(var5.hasNext()) {
        // 獲取ApplicationListener進行遍歷
        ApplicationListener<?> listener = (ApplicationListener)var5.next();
        // 判斷是否能夠用executor異步執行,能夠的話使用executor進行異步派發
        if (executor != null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            // 不然同步執行
            this.invokeListener(listener, event);
        }
    }
}
複製代碼

執行invokeListener方法,拿到listener回調onApplicationEvent方法

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        // 執行invokeListener方法,拿到listener回調onApplicationEvent方法
        listener.onApplicationEvent(event);
    } catch (ClassCastException var6) {
        String msg = var6.getMessage();
        if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
            throw var6;
        }

        Log logger = LogFactory.getLog(this.getClass());
        if (logger.isTraceEnabled()) {
            logger.trace("Non-matching event type for listener: " + listener, var6);
        }
    }
}
複製代碼

【2】獲取事件派發器getApplicationEventMulticaster

容器建立對象,調用refresh()方法——>initApplicationEventMulticaster()方法,初始化ApplicationEventMulticaster

  • 先從容器中找是否有ID爲「applicationEventMulticaster」的組件
  • 有,則經過getBean的方式獲取到該組件
  • 若是沒有,則建立一個簡單的ApplicationEventMulticaster
  • 將建立的註冊到容器的單實例bean中,這樣咱們就能夠在其餘組件要派發事件,自動注入這個applicationEventMulticaster
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    // 從bean工廠中找是否有ID爲「applicationEventMulticaster」的組件
    if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
        // 獲取到該組件
        this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    } else {
        // 若是沒有則本身建立一個簡單的ApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        // 將建立的註冊到容器的單實例bean中
        beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}
複製代碼

【3】容器中有哪些監聽器

容器建立對象,調用refresh()方法——>registerListeners()方法,註冊監聽器

  • getApplicationListeners:獲取全部Listener
  • 從容器中拿到全部ApplicationListener類型的Listener組件
  • 把組件添加到getApplicationEventMulticaster派發器中,註冊到派發器中
protected void registerListeners() {
    // 獲取全部Listener
    Iterator var1 = this.getApplicationListeners().iterator();

    while(var1.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var1.next();
        this.getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // 從容器中拿到全部ApplicationListener類型的Listener組件
    String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
    String[] var7 = listenerBeanNames;
    int var3 = listenerBeanNames.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String listenerBeanName = var7[var4];
        // 把組件添加到getApplicationEventMulticaster派發器中,註冊到派發器中
        this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        Iterator var9 = earlyEventsToProcess.iterator();

        while(var9.hasNext()) {
            ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
            this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }

}
複製代碼

4、總結

事件發佈流程:

  1. refresh():容器建立對象
  2. finishRefresh():容器完成刷新
  3. publishEvent(new ContextRefreshedEvent()):發佈事件,將容器刷新完成事件做爲參數執行
    1. getApplicationEventMulticaster():獲取到事件的多播器(派發器),就是把事件發送到多個監聽器讓他們同時感知
    2. multicastEvent:派發事件
    3. 經過循環獲取到全部的ApplicationListener並進行判斷
      1. 若是有executor,能夠支持使用executor進行異步派發
      2. 不然,同步的方式直接執行listener方法,拿到listener回調onApplicationEvent方法
相關文章
相關標籤/搜索