<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之事件機(jī)制詳解

          共 11004字,需瀏覽 23分鐘

           ·

          2021-08-01 16:39

          機(jī)制詳解

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

          下面我們對(duì)上圖中涉及到的幾個(gè)類進(jìn)行講解。
          ApplicationEvent:
          抽象類,繼承了JDK的EventObject接口,起到包裝事件源的作用。
          ApplicationListener:
          實(shí)現(xiàn)了JDK的EventListener接口,起到監(jiān)聽(tīng)器的作用。

          ApplicationEventMulticaster類:
          在觀察者模式中,一定要有一個(gè)管理維護(hù)監(jiān)聽(tīng)者列表的功能。在Spring的事件機(jī)制中,將維護(hù)監(jiān)聽(tīng)者列表的功能單獨(dú)定義了一個(gè)接口,即ApplicationEventMulticaster接口。這也體現(xiàn)了單一責(zé)任原則的設(shè)計(jì)思想。我們看其源碼:

          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.
            * <p>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.
            * <p>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.
            * <p>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);

          }


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

          public interface ApplicationEventPublisher {

           /**
            * Notify all <strong>matching</strong> listeners registered with this
            * application of an application event. Events may be framework events
            * (such as ContextRefreshedEvent) or application-specific events.
            * <p>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 <strong>matching</strong> listeners registered with this
            * application of an event.
            * <p>If the specified {@code event} is not an {@link ApplicationEvent},
            * it is wrapped in a {@link PayloadApplicationEvent}.
            * <p>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方法,進(jìn)行事件的發(fā)布。但是事件不是直接發(fā)布到listener中,而是發(fā)布在ApplicationEventMulticaster類中,所以在ApplicationEventPublisher類中,一定會(huì)有ApplicationEventMulticaster對(duì)象,將事件發(fā)布到ApplicationEventMulticaster中。

          事件流程總結(jié):
          通過(guò)上面幾個(gè)類的描述,我們總結(jié)一下spring事件機(jī)制的流程:
          流程的核心,就是PublishEvent。Event對(duì)象以參數(shù)的形式傳入PublishEvent對(duì)象。然后將Event事件傳入ApplicationEventMulticaster類中,由ApplicationEventMulticaster類將事件傳給其維護(hù)的監(jiān)聽(tīng)者,執(zhí)行監(jiān)聽(tīng)者方法。

          領(lǐng)悟

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

          應(yīng)用

          上面分析了Spring事件機(jī)制的運(yùn)行原理,那么對(duì)我們實(shí)際開(kāi)發(fā)中,有何幫助呢?
          這就需要我們結(jié)合源碼進(jìn)行解讀了。
          筆者找到一篇寫的很好的博文,大家可以參考:淺談Spring事件監(jiān)聽(tīng)。

          我們可以自定義事件源,如下:

          @Component
          public class MyEventSource {
              public void ccc(){
                  System.out.println("事件源方法");
              }
          }


          然后定義Event對(duì)象,包裝事件源:

          @Component
          public class MyEvent extends ApplicationEvent {
              public MyEvent(MyEventSource source) {
                  super(source);
              }
              
              public void eventMethod(){
                  System.out.println("事件自定義方法");
              }
          }

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

          下面,我們定義監(jiān)聽(tīng)者:


          @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)聽(tīng)者發(fā)生一些改變");
                  }
              }
          }



          需要注意的是,如果我們讓Spring自帶的事件發(fā)布類去發(fā)布事件,上面我們提到,會(huì)發(fā)布到ApplicationEventMulticaster 中,而Spring默認(rèn)的ApplicationEventMulticaster 類會(huì)把所有的事件都發(fā)布給監(jiān)聽(tīng)者,它并不會(huì)去做過(guò)濾,什么事件發(fā)給什么監(jiān)聽(tīng)者。所以我們只能在每個(gè)監(jiān)聽(tīng)者方法中去做判斷,事件類型屬于某個(gè)類型時(shí),才做處理。
          如果我們想特定的事件發(fā)布給特定的監(jiān)聽(tīng)者,那我們只能自己實(shí)現(xiàn)Spring的發(fā)布類和ApplicationEventMulticaster類,自己定義事件的發(fā)布機(jī)制。

          更正:
          ApplicationListener類是支持泛型的,在類后定義泛型,可以過(guò)濾掉其他的事件對(duì)象,只接收泛型類事件。

          最后,也是最關(guān)鍵的步驟,萬(wàn)事俱備只欠東風(fēng)了,事件的發(fā)布,是需要我們?cè)跇I(yè)務(wù)代碼中自行進(jìn)行發(fā)布的,這里我們用ApplicationContext作為發(fā)布者,進(jìn)行事件的發(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事件機(jī)制其實(shí)就是發(fā)布-訂閱思想的一個(gè)體現(xiàn),只不過(guò),Spring中的訂閱發(fā)布只限于在一個(gè)Spring容器中完成。而我們平時(shí)使用的訂閱發(fā)布,都是分布式環(huán)境下的,一般采用MQ的方式完成。在實(shí)際的應(yīng)用開(kāi)發(fā)中,一個(gè)容器中進(jìn)行訂閱發(fā)布的需求并不是很多,當(dāng)有些特殊需求可以轉(zhuǎn)化成容器內(nèi)的訂閱發(fā)布時(shí),希望大家可以想到應(yīng)用Spring提供的事件機(jī)制。
          在單機(jī)環(huán)境中,我們要實(shí)現(xiàn)代碼的解耦,可以采用事件機(jī)制。例如:在前面講觀察者模式時(shí),我們把監(jiān)聽(tīng)者的放在事件類中維護(hù)的方式,就是高耦合的。而Spring將其進(jìn)行了解耦。

          補(bǔ)充:

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

          @Component
          public class MyListener implements ApplicationListener<ContextRefreshedEvent> {

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

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


            作者 |  敲代碼的小小酥

          來(lái)源 |  csdn.net/qq1309664161/article/details/119025323


          最近給大家找了  通用權(quán)限系統(tǒng)


          資源,怎么領(lǐng)???


          掃二維碼,加我微信,回復(fù):通用權(quán)限系統(tǒng)

           注意,不要亂回復(fù) 

          沒(méi)錯(cuò),不是機(jī)器人
          記得一定要等待,等待才有好東西
          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  欧美V日韩V国产V | 人妻少妇内射无码一区二区 | 天天日狠狠的 | 操逼无码免费 | 波多野结衣无码NET,AV |