天天看點

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

之前做個資料統計的需求,就是用的spring事件釋出與監聽機制(用于采集基礎資料),今天做個小小的總結。

下面我們先回顧下基礎的spring知識(beanfactory&applicationcontext的相關知識),因為spring事件釋出與監聽機制就包含在其中初始化流程的某個步驟中。

beanfacotry與applicationcontext

1、beanfactory 使用demo:

1.1 beanfactory 源碼(節選getbean()方法)

1.2 代碼分析:

此處,我們通過構造 beanfactory 的 執行個體對象 xmlbeanfactory,完成對特定xml檔案的bean資訊加載,然後通過 getbean(string beanname); 方法擷取特定的對象。

beanfactory在啟動的時候不會去執行個體化bean,二是隻有從容器中取bean的時候才會去執行個體化;

beanfactory具備延遲執行個體化的優點;

同時,beanfactory也具備不能及時發現一些存在的spring的配置問題的缺點;

2、applicationcontext 使用demo:

2.1 applicationcontext 源碼

applicationcontext 接口的關系架構圖:(applicationcontext 本質是對 beanfactory 進行了功能拓展)

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

applicationcontext跟beanfactory 相反,它是在容器啟動時,一次性建立了所有的bean。同時,注冊spring監聽器的工作也發生在這裡:registerlisteners();

2.2 代碼分析:

梳理下源碼的流程,見下圖:(我們這章節的重點就是從10步進行切入:注冊事件監聽器)

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

3、beanfacotry與applicationcontext的差別

beanfacotry是spring中比較原始的factory。如xmlbeanfactory就是一種典型的beanfactory。

原始的beanfactory無法支援spring的許多插件,如aop功能、web應用等。

applicationcontext接口,它由beanfactory接口派生而來,因而提供beanfactory所有的功能。

applicationcontext以一種更向面向架構的方式工作以及對上下文進行分層和實作繼承,applicationcontext包還提供了以下的功能:  1)messagesource, 提供國際化的消息通路   2)資源通路,如url和檔案    3)事件傳播 (我們這章節的重點)  4)載入多個(有繼承關系)上下文 ,使得每一個上下文都專注于一個特定的層次,比如應用的web層。

spring事件釋出與監聽的應用

1、事件源&事件pojo

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

2、事件監聽器

applicationlistener 需要設定泛型限定類,也就是上面提到的事件源。

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

3、釋出自定義事件

在業務的需要地方進行事件釋出:

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

4、代碼分析

applicationcontext事件機制是觀察者設計模式的實作。

通過applicationevent類和applicationlistener接口,可以實作applicationcontext事件處理。

如果容器中有一個applicationlistener bean,每當applicationcontext釋出applicationevent時,applicationlistener bean将自動被觸發(同步/異步的方式)。

兩個重要成員

applicationevent:容器事件,必須由applicationcontext釋出;

applicationlistener:監聽器,可由容器中的任何監聽器bean擔任。

源碼剖析

源碼剖析思路,以3個元件作為線索:

applicationevent 事件

applicationlistener 監聽器,對事件進行監聽

applicationeventmulticaster 事件廣播器,将publish的事件廣播給所有的監聽器。

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

元件一:事件 applicationevent 的5種實作

applicationevent 是所有事件的基礎抽象類,包括我們的自定義事件也是繼承了它。

contextrefreshedevent :當applicationcontext初始化或者重新整理,将會釋出,例如使用configurableapplicationcontext接口調用refresh方法,初始化意味着加載所有的bean,同時

contextstartedevent:當applicationcontext啟動的時候,将會調用start方法,釋出此事件。

contextstoppedevent:當容器停止的時候,釋出事件。

contextclosedevent:當容器關閉的時候,釋出事件。

requesthandledevent:http請求完成後,釋出事件。

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?
面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

元件二:監聽器 applicationlistener

applicationlistener:applicationcontext容器内部自定義事件監聽器接口,繼承自java.util.eventlistener,applicationcontext容器在啟動時,會自動識别并加載eventlistener類型bean的定義,一旦容器事件釋出,将會通知注冊到容器的監聽器。

面試官來了:說說你對Spring事件釋出&監聽源碼的了解?
面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

元件三:廣播器applicationeventmulticaster

1、概述:釋出器 applicationeventpublisher 和 廣播器applicationeventmulticaster 的關系

applicationeventpublisher:是一個封裝事件釋出接口,作為applicationcontext父類接口。

applicationeventmulticaster:管理applicationlistener對象,并且釋出它們。

applicationcontext 委托給了 abstractapplicationeventmulticaster 來實作事件監聽器(applicationlistener)的管理。

2、釋出事件 - applicationeventpublisher

源碼 :使用了 applicationeventpublisher.publishevent() 的代碼段,可以将事件釋出出去。

    源碼分析:

getapplicationeventmulticaster().multicastevent(applicationevent, eventtype);

擷取到廣播器(simpleapplicationeventmulticaster),并且将自定義事件告訴廣播器。

3、廣播器 - simpleapplicationeventmulticaster

3.1、simpleapplicationeventmulticaster 廣播器的類架構圖:

applicationeventmulticaster 接口實作類是 simpleapplicationeventmulticaster,它的 multicastevent() 方法功能是:實作了周遊監聽器清單,逐個釋出事件到監聽器中(觀察者模式的應用場景)。
面試官來了:說說你對Spring事件釋出&監聽源碼的了解?

代碼分析:上文提及的代碼段:getapplicationeventmulticaster() 方法便是擷取到注入的執行個體 simpleapplicationeventmulticaster,它即是applicationeventmulticaster 的實作類了。3.2、simpleapplicationeventmulticaster 内部維護了一個監聽器清單,即是一個 concurrenthashmap 進行管理的。

<code>final map&lt;listenercachekey, cachedlistenerretriever&gt; retrievercache = new concurrenthashmap&lt;&gt;(64);</code>3.3、simpleapplicationeventmulticaster 廣播事件源碼,通過multicastevent() 方法實作

代碼解析:最終調用 simpleapplicationeventmulticaster 的 invokelistener() 方法進行實質事件處理。

面試官來了:說說你對Spring事件釋出&amp;監聽源碼的了解?

simpleapplicationeventmulticaster的 invokelistener() 方法源碼,最終調用了 doinvokelistener() 方法。 

doinvokelistener() 最終會調用監聽器的 onapplicationevent 方法,實作監聽效果。這裡注意,方法會抛出 classcastexception 異常,因為事件源被業務處理時可能發生類型轉換失敗的情況,這樣也能夠捕獲到這類運作時異常。

代碼分析:

再走讀一下源碼,我們可以發現 simpleapplicationeventmulticaster 其實是支援異步事件通知 和同步事件通知。

而 simpleapplicationeventmulticaster 作為預設的事件廣播器,用的是同步通知的方式;但是spring給我們提供了一個解決方案來實作我們需要的異步廣播器(參考下面的小點)。

面試官來了:說說你對Spring事件釋出&amp;監聽源碼的了解?

實作自定義異步廣播器?

1、自定義廣播器

@component("applicationeventmulticaster") 注解則聲明了bean的name為固定的“applicationeventmulticaster”。

2、源碼分析(自定義廣播器是如何被注冊到spring容器的)

2.1、我們走讀一下 abstractapplicationcontext 的源碼,注意到一個靜态字元串變量的值為“applicationeventmulticaster”;

2.2、同時定位到 initapplicationeventmulticaster() 方法的作用就是 initialize the applicationeventmulticaster.(初始化事件廣播器);如果可以擷取到則使用這個“applicationeventmulticaster” bean,則可以進行注冊了(其實就是擷取對象引用然後指派)。

代碼分析:當在我們自定義的多點傳播器中設定了executor時,simpleapplicationeventmulticaster 廣播器的exeutor就不為空了 ,就會走到第一個異步多點傳播的路徑。

總結

通過上文,我們複習了spring的兩大容器元件(beanfacotry與applicationcontext);進而切入到“spring事件釋出&amp;監聽機制”:包括了它們的應用案例、底層源碼分析;最後針對spring事件廣播器的特性,拓展了如何自定義異步廣播器以及它背後的原理。希望對大家有所幫助。

掃描二維碼

擷取技術幹貨

背景技術彙

面試官來了:說說你對Spring事件釋出&amp;監聽源碼的了解?

繼續閱讀