Spring 事件機(jī)制源碼分析

前言

上一篇 Spring 事件機(jī)制概述 的文章中枷莉,從觀察者模式熬词、Java 事件機(jī)制轮听、Spring 事件機(jī)制的具體代碼實現(xiàn)進(jìn)行了簡要的分析卷谈。本篇文章將在其基礎(chǔ)上對 Spring 事件機(jī)制的源碼進(jìn)行分析杯拐。

  • Spring 事件機(jī)制流程回顧
    1. 創(chuàng)建一個具體的事件類,該類需繼承 ApplicationEvent雏搂;
    2. 創(chuàng)建一個針對某個特定時間的監(jiān)聽器實現(xiàn) ApplicationListener藕施,并配置 @Component 注解,確保 Ioc 容器啟動時凸郑,監(jiān)聽器會注入至 Ioc 容器裳食;
    3. 初始化 Ioc 容器;
    4. 由于 ApplicationContext 實現(xiàn)了 ApplicationEventPublisher芙沥,因此直接使用 ApplicationContext 作為事件發(fā)布器發(fā)布某個事件诲祸,此時該事件的監(jiān)聽器便會接收到事件并做出相應(yīng)的處理。此處也可以通過實現(xiàn) ApplicationEventPublisherAware 接口而昨,來獲得事件發(fā)布器救氯。

上述的流程中,可能會有這樣一些疑問:

  1. 事件監(jiān)聽器是何時被注入的歌憨?
  2. 事件發(fā)布器是怎么樣對具體的事件進(jìn)行發(fā)布着憨?

帶著這兩個疑問,開始源碼的分析务嫡。

Spring 事件機(jī)制源碼分析

在實際應(yīng)用的代碼中甲抖,Ioc 容器 ApplicationContext 創(chuàng)建完成后,監(jiān)聽器 ApplicationListener 及發(fā)布器 ApplicationEventPublisher 均已就緒心铃,可直接使用進(jìn)行事件發(fā)布准谚,故從 ApplicationContext 初始化著手來分析。 通過對 Spring Ioc 的源碼分析去扣,我們知道了容器初始化的核心方法為 AbstractApplicationContext::refresh柱衔,查看 refresh 方法,我們發(fā)現(xiàn)有兩個方法與 Spring 的事件相關(guān)愉棱。

  1. initApplicationEventMulticaster 方法

    • ApplicationEventMulticaster(事件多播器)
      ApplicationEventMulticaster 接口的方法主要是操作 ApplicationListener, 廣播 ApplicationEvent

      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);
      }
      

      分析其繼承關(guān)系唆铐,在抽象類 AbstractApplicationEventMulticaster 中實現(xiàn)了接口的方法,SimpleApplicationEventMulticaster 中引入異步操作的支持奔滑。相關(guān)源碼會在后面串聯(lián)分析或链。


      ApplicationEventMulticaster
    • 源碼
      源碼的核心流程是,先判斷 BeanFactory 中有沒有 ApplicationEventMulticaster 類档押,若有則賦值給本地變量,若無則創(chuàng)建 SimpleApplicationEventMulticaster 并賦值給本地變量。

      protected void initApplicationEventMulticaster() {
          ConfigurableListableBeanFactory beanFactory = getBeanFactory();
          if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
              this.applicationEventMulticaster =
                      beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
              if (logger.isDebugEnabled()) {
                  logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
              }
          }
          else {
              this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
              beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
              if (logger.isDebugEnabled()) {
                  logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                          APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                          "': using default [" + this.applicationEventMulticaster + "]");
              }
          }
      }
      
  2. registerListeners

    • 源碼分析
      從 registerListeners 源碼中可以看到令宿,該方法中只是將 ApplicationListener 對應(yīng)的 BeanName 保存起來了叼耙,因此這個時候 Bean 都還沒有完成初始化,只有 beanDefinition 的信息粒没,后續(xù)在完成 Bean 初始化后筛婉,會調(diào)用一個后置處理器 ApplicationListenerDetector 的 postProcessAfterInitialization 方法,將 ApplicationListener 對應(yīng)的 Bean 實例綁定到 ApplicationEventMulticaster 中癞松。
    protected void registerListeners() {
        // Register statically specified listeners first.
        // getApplicationListeners 方法中返回本地的 applicationListeners
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }
    
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        // 從 BeanFactory 中找到 ApplicationListener 類所對應(yīng)的所有 BeanName
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }
    
        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (earlyEventsToProcess != null) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }
    

在完成 ApplicationEventMulticaster 初始化爽撒,監(jiān)聽器注入后,后續(xù)就是如何發(fā)布事件响蓉,從 ApplicationContext 的類繼承關(guān)系中知道硕勿,該類繼承了 ApplicationEventPublisher,在 AbstractApplicationContext 類中實現(xiàn)了方法 publishEvent枫甲,具體源碼為:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }

    // Decorate event as an ApplicationEvent if necessary
    ApplicationEvent applicationEvent;
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent) event;
    }
    else {
        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 {
        // 使用事件廣播器廣播該事件
        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);
        }
    }
}

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // getApplicationListeners 根據(jù) event 的類型找到相應(yīng)的 listener
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        // 增加了對異步事件的支持源武,如果 executor 不為空則異步通知該事件
        Executor executor = getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            invokeListener(listener, event);
        }
    }
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = getErrorHandler();
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        doInvokeListener(listener, event);
    }
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        listener.onApplicationEvent(event);
    }
    catch (ClassCastException ex) {
        String msg = ex.getMessage();
        if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
            // Possibly a lambda-defined listener which we could not resolve the generic event type for
            // -> let's suppress the exception and just log a debug message.
            Log logger = LogFactory.getLog(getClass());
            if (logger.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, ex);
            }
        }
        else {
            throw ex;
        }
    }
}
總結(jié)

從上述的分析中可知,Spring 事件發(fā)布的主要流程為:

  1. 初始化 事件多播器(ApplicationEventMulticaster)
  2. 注冊 ApplicationListener
  3. 調(diào)用后置處理器 ApplicationListenerDetector 完成 ApplicationEventMulticaster 中 listener 實例的賦值想幻;
  4. 發(fā)布事件時粱栖,調(diào)用 ApplicationEventMulticaster 的廣播方法,將 Event 廣播至對應(yīng)的 Listener脏毯。

Spring 提供了以下 5 中標(biāo)準(zhǔn)的事件闹究,我們可以注冊響應(yīng)的監(jiān)聽器進(jìn)行處理該事件。

  1. 上下文更新事件(ContextRefreshedEvent):在調(diào)用ConfigurableApplicationContext 接口中的refresh()方法時被觸發(fā)食店。
  2. 上下文開始事件(ContextStartedEvent):當(dāng)容器調(diào)用ConfigurableApplicationContext的Start()方法開始/重新開始容器時觸發(fā)該事件渣淤。
  3. 上下文停止事件(ContextStoppedEvent):當(dāng)容器調(diào)用ConfigurableApplicationContext的Stop()方法停止容器時觸發(fā)該事件。
  4. 上下文關(guān)閉事件(ContextClosedEvent):當(dāng)ApplicationContext被關(guān)閉時觸發(fā)該事件叛买。容器被關(guān)閉時砂代,其管理的所有單例Bean都被銷毀。
  5. 請求處理事件(RequestHandledEvent):在Web應(yīng)用中率挣,當(dāng)一個http請求(request)結(jié)束觸發(fā)該事件刻伊。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市椒功,隨后出現(xiàn)的幾起案子捶箱,更是在濱河造成了極大的恐慌,老刑警劉巖动漾,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丁屎,死亡現(xiàn)場離奇詭異,居然都是意外死亡旱眯,警方通過查閱死者的電腦和手機(jī)晨川,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門证九,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人共虑,你說我怎么就攤上這事愧怜。” “怎么了妈拌?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵拥坛,是天一觀的道長。 經(jīng)常有香客問我尘分,道長猜惋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任培愁,我火速辦了婚禮著摔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竭钝。我一直安慰自己梨撞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布香罐。 她就那樣靜靜地躺著卧波,像睡著了一般。 火紅的嫁衣襯著肌膚如雪庇茫。 梳的紋絲不亂的頭發(fā)上港粱,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音旦签,去河邊找鬼查坪。 笑死,一個胖子當(dāng)著我的面吹牛宁炫,可吹牛的內(nèi)容都是我干的偿曙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼羔巢,長吁一口氣:“原來是場噩夢啊……” “哼望忆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起竿秆,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤启摄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幽钢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歉备,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年匪燕,在試婚紗的時候發(fā)現(xiàn)自己被綠了蕾羊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喧笔。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖龟再,靈堂內(nèi)的尸體忽然破棺而出溃斋,到底是詐尸還是另有隱情,我是刑警寧澤吸申,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站享甸,受9級特大地震影響截碴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛉威,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一日丹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蚯嫌,春花似錦哲虾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至栅盲,卻和暖如春汪诉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谈秫。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工扒寄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拟烫。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓该编,卻偏偏與公主長得像,于是被迫代替她去往敵國和親硕淑。 傳聞我的和親對象是個殘疾皇子课竣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容