<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring之事件機制詳解

          共 7099字,需瀏覽 15分鐘

           ·

          2021-11-28 11:57


          源?/?CSDN? ? ? ??文/?敲代碼的小小酥

          機制詳解
          Spring提供了事件機制,其本質(zhì)是JDK提供的事件機制的應(yīng)用,利用的是觀察者設(shè)計模式,具體請看設(shè)計模式之觀察者模式(Observer Pattern)。
          這里我們來分析Spring事件機制的原理。
          先上UML圖,不熟悉UML規(guī)則的可以看UML類圖的制作規(guī)則。

          下面我們對上圖中涉及到的幾個類進行講解。
          ApplicationEvent:
          抽象類,繼承了JDK的EventObject接口,起到包裝事件源的作用。
          ApplicationListener:
          實現(xiàn)了JDK的EventListener接口,起到監(jiān)聽器的作用。
          ApplicationEventMulticaster類:
          在觀察者模式中,一定要有一個管理維護監(jiān)聽者列表的功能。在Spring的事件機制中,將維護監(jiān)聽者列表的功能單獨定義了一個接口,即ApplicationEventMulticaster接口。這也體現(xiàn)了單一責(zé)任原則的設(shè)計思想。我們看其源碼:
          public?interface?ApplicationEventMulticaster?{

          ?/**
          ??*?Add?a?listener?to?be?notified?of?all?events.
          ??*?@param?listener?the?listener?to?add
          ??*/
          ?void?addApplicationListener(ApplicationListener?listener);

          ?/**
          ??*?Add?a?listener?bean?to?be?notified?of?all?events.
          ??*?@param?listenerBeanName?the?name?of?the?listener?bean?to?add
          ??*/
          ?void?addApplicationListenerBean(String?listenerBeanName);

          ?/**
          ??*?Remove?a?listener?from?the?notification?list.
          ??*?@param?listener?the?listener?to?remove
          ??*/
          ?void?removeApplicationListener(ApplicationListener?listener);

          ?/**
          ??*?Remove?a?listener?bean?from?the?notification?list.
          ??*?@param?listenerBeanName?the?name?of?the?listener?bean?to?remove
          ??*/
          ?void?removeApplicationListenerBean(String?listenerBeanName);

          ?/**
          ??*?Remove?all?listeners?registered?with?this?multicaster.
          ??*?

          After?a?remove?call,?the?multicaster?will?perform?no?action
          ??*?on?event?notification?until?new?listeners?are?registered.
          ??*/
          ?void?removeAllListeners();

          ?/**
          ??*?Multicast?the?given?application?event?to?appropriate?listeners.
          ??*?

          Consider?using?{@link?#multicastEvent(ApplicationEvent,?ResolvableType)}
          ??*?if?possible?as?it?provides?better?support?for?generics-based?events.
          ??*?@param?event?the?event?to?multicast
          ??*/
          ?void?multicastEvent(ApplicationEvent?event);

          ?/**
          ??*?Multicast?the?given?application?event?to?appropriate?listeners.
          ??*?

          If?the?{@code?eventType}?is?{@code?null},?a?default?type?is?built
          ??*?based?on?the?{@code?event}?instance.
          ??*?@param?event?the?event?to?multicast
          ??*?@param?eventType?the?type?of?event?(can?be?{@code?null})
          ??*?@since?4.2
          ??*/
          ?void?multicastEvent(ApplicationEvent?event,?@Nullable?ResolvableType?eventType);

          }


          可以看到,這個接口定義了增刪查監(jiān)聽者的方法,所以,監(jiān)聽者列表的維護通過這個接口實現(xiàn)。需要注意的是這個接口還定義了multicastEvent方法。通過這個方法,將事件傳給監(jiān)聽器。所以這個類,將事件和監(jiān)聽器,連接在一起。這里采用的是中介者模式,這個接口就是中介者角色。
          ApplicationEventPublisher:
          Spring設(shè)計的事件發(fā)布類,我們看其源碼:
          public?interface?ApplicationEventPublisher?{

          ?/**
          ??*?Notify?all?matching?listeners?registered?with?this
          ??*?application?of?an?application?event.?Events?may?be?framework?events
          ??*?(such?as?ContextRefreshedEvent)?or?application-specific?events.
          ??*?

          Such?an?event?publication?step?is?effectively?a?hand-off?to?the
          ??*?multicaster?and?does?not?imply?synchronous/asynchronous?execution
          ??*?or?even?immediate?execution?at?all.?Event?listeners?are?encouraged
          ??*?to?be?as?efficient?as?possible,?individually?using?asynchronous
          ??*?execution?for?longer-running?and?potentially?blocking?operations.
          ??*?@param?event?the?event?to?publish
          ??*?@see?#publishEvent(Object)
          ??*?@see?org.springframework.context.event.ContextRefreshedEvent
          ??*?@see?org.springframework.context.event.ContextClosedEvent
          ??*/
          ?default?void?publishEvent(ApplicationEvent?event)?{
          ??publishEvent((Object)?event);
          ?}

          ?/**
          ??*?Notify?all?matching?listeners?registered?with?this
          ??*?application?of?an?event.
          ??*?

          If?the?specified?{@code?event}?is?not?an?{@link?ApplicationEvent},
          ??*?it?is?wrapped?in?a?{@link?PayloadApplicationEvent}.
          ??*?

          Such?an?event?publication?step?is?effectively?a?hand-off?to?the
          ??*?multicaster?and?does?not?imply?synchronous/asynchronous?execution
          ??*?or?even?immediate?execution?at?all.?Event?listeners?are?encouraged
          ??*?to?be?as?efficient?as?possible,?individually?using?asynchronous
          ??*?execution?for?longer-running?and?potentially?blocking?operations.
          ??*?@param?event?the?event?to?publish
          ??*?@since?4.2
          ??*?@see?#publishEvent(ApplicationEvent)
          ??*?@see?PayloadApplicationEvent
          ??*/
          ?void?publishEvent(Object?event);

          }

          里面定義了publishEvent方法,進行事件的發(fā)布。但是事件不是直接發(fā)布到listener中,而是發(fā)布在ApplicationEventMulticaster類中,所以在ApplicationEventPublisher類中,一定會有ApplicationEventMulticaster對象,將事件發(fā)布到ApplicationEventMulticaster中。
          事件流程總結(jié):
          通過上面幾個類的描述,我們總結(jié)一下spring事件機制的流程:
          流程的核心,就是PublishEvent。Event對象以參數(shù)的形式傳入PublishEvent對象。然后將Event事件傳入ApplicationEventMulticaster類中,由ApplicationEventMulticaster類將事件傳給其維護的監(jiān)聽者,執(zhí)行監(jiān)聽者方法。

          領(lǐng)悟

          由上面Spring設(shè)計事件模式思路我們可以感受到,Spring把單一的功能,都拎出來形成了一套接口規(guī)范,然后多個接口規(guī)范組合,去完成一件事情。所以我們在閱讀源碼時會感覺很亂。只要我們分析清楚每個對象的設(shè)計思路和作用是什么,再分析他們之間的組合完成了什么事情,就很容易理解其設(shè)計理念了。

          應(yīng)用

          上面分析了Spring事件機制的運行原理,那么對我們實際開發(fā)中,有何幫助呢?
          這就需要我們結(jié)合源碼進行解讀了。
          筆者找到一篇寫的很好的博文,大家可以參考:淺談Spring事件監(jiān)聽。
          我們可以自定義事件源,如下:
          @Component
          public?class?MyEventSource?{
          ????public?void?ccc(){
          ????????System.out.println("事件源方法");
          ????}
          }


          然后定義Event對象,包裝事件源:
          @Component
          public?class?MyEvent?extends?ApplicationEvent?{
          ????public?MyEvent(MyEventSource?source)?{
          ????????super(source);
          ????}
          ????
          ????public?void?eventMethod(){
          ????????System.out.println("事件自定義方法");
          ????}
          }

          這里我們需要注意,有參構(gòu)造中,傳入事件源,我們還可以在事件類中定義其他的方法,在監(jiān)聽者中調(diào)用。因為監(jiān)聽者監(jiān)聽的是事件類,所以可以直接調(diào)到事件類的自定義方法。
          下面,我們定義監(jiān)聽者:

          @Component
          public?class?MyListener?implements?ApplicationListener?{
          ????@Override
          ????public?void?onApplicationEvent(ApplicationEvent?applicationEvent)?{
          ????????if(applicationEvent?instanceof?MyEvent)?{
          ????????????((MyEvent)applicationEvent).eventMethod();
          ????????????MyEventSource?eventSource?=?(MyEventSource)?applicationEvent.getSource();
          ????????????eventSource.ccc();
          ????????????System.out.println("監(jiān)聽者發(fā)生一些改變");
          ????????}
          ????}
          }



          需要注意的是,如果我們讓Spring自帶的事件發(fā)布類去發(fā)布事件,上面我們提到,會發(fā)布到ApplicationEventMulticaster 中,而Spring默認的ApplicationEventMulticaster 類會把所有的事件都發(fā)布給監(jiān)聽者,它并不會去做過濾,什么事件發(fā)給什么監(jiān)聽者。所以我們只能在每個監(jiān)聽者方法中去做判斷,事件類型屬于某個類型時,才做處理。
          如果我們想特定的事件發(fā)布給特定的監(jiān)聽者,那我們只能自己實現(xiàn)Spring的發(fā)布類和ApplicationEventMulticaster類,自己定義事件的發(fā)布機制。
          更正:
          ApplicationListener類是支持泛型的,在類后定義泛型,可以過濾掉其他的事件對象,只接收泛型類事件。
          最后,也是最關(guān)鍵的步驟,萬事俱備只欠東風(fēng)了,事件的發(fā)布,是需要我們在業(yè)務(wù)代碼中自行進行發(fā)布的,這里我們用ApplicationContext作為發(fā)布者,進行事件的發(fā)布,代碼如下:
          public?class?MyTest?{

          ????@Test
          ????public?void?test1()?{
          ????????ClassPathXmlApplicationContext?applicationContext?=?new?ClassPathXmlApplicationContext("spring.xml");
          ????????Object?o=applicationContext.getBean("myEvent");
          ????????applicationContext.publishEvent(applicationContext.getBean("myEvent"));
          ???????

          ????}
          }

          總結(jié)

          Spring事件機制其實就是發(fā)布-訂閱思想的一個體現(xiàn),只不過,Spring中的訂閱發(fā)布只限于在一個Spring容器中完成。而我們平時使用的訂閱發(fā)布,都是分布式環(huán)境下的,一般采用MQ的方式完成。在實際的應(yīng)用開發(fā)中,一個容器中進行訂閱發(fā)布的需求并不是很多,當有些特殊需求可以轉(zhuǎn)化成容器內(nèi)的訂閱發(fā)布時,希望大家可以想到應(yīng)用Spring提供的事件機制。
          在單機環(huán)境中,我們要實現(xiàn)代碼的解耦,可以采用事件機制。例如:在前面講觀察者模式時,我們把監(jiān)聽者的放在事件類中維護的方式,就是高耦合的。而Spring將其進行了解耦。

          補充:

          Spring為我們提供了一些事件類。我們可以利用這些現(xiàn)成的事件,自定義監(jiān)聽者,來做一些業(yè)務(wù)。下面我們介紹常用的事件。
          ContextRefreshedEvent(上下文更新完成事件):在spring容器初始化流程完成后,觸發(fā)事件。事件的觸發(fā)(即調(diào)用publishEvent方法)是Spring自己調(diào)用的,我們只需定義Listener監(jiān)聽者處理業(yè)務(wù)即可。
          如之前一個項目,在項目啟動的時候有一個while(true){…}死循環(huán),造成了項目無法正常啟動。當時的解決方案是將死循環(huán)另起了一個線程解決的。現(xiàn)在可以采用ContextRefreshedEvent事件解決。當容器加載完成后,再執(zhí)行死循環(huán)業(yè)務(wù)。代碼如下:
          @Component
          public?class?MyListener?implements?ApplicationListener?{

          ????@Override
          ????public?void?onApplicationEvent(ContextRefreshedEvent?event)?{
          ??????while?(true){
          ??????????System.out.println("執(zhí)行業(yè)務(wù)");
          ??????}
          ????}
          }

          RequestHandledEvent:在web應(yīng)用中,一個http請求結(jié)束觸發(fā)該事件。在一些特殊場景里,可能會應(yīng)用到該事件。

          ? 作者?|??敲代碼的小小酥
          來源 |??csdn.net/qq1309664161/article/details/119025323



          END


          頂級程序員:topcoding

          做最好的程序員社區(qū):Java后端開發(fā)、Python、大數(shù)據(jù)、AI


          一鍵三連「分享」、「點贊」和「在看」


          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美深夜福利 | 久操免费看 | 超碰人人干人人操 | 狠狠草在线观看 | 黄片试看 |