天天看點

Spring之事件機制詳解

Spring之事件機制詳解

機制詳解

Spring提供了事件機制,其本質是JDK提供的事件機制的應用,利用的是觀察者設計模式,具體請看設計模式之觀察者模式(Observer Pattern)。

這裡我們來分析Spring事件機制的原理。

先上UML圖,不熟悉UML規則的可以看UML類圖的制作規則。

Spring之事件機制詳解

下面我們對上圖中涉及到的幾個類進行講解。

ApplicationEvent:

抽象類,繼承了JDK的EventObject接口,起到包裝事件源的作用。

ApplicationListener:

實作了JDK的EventListener接口,起到監聽器的作用。

ApplicationEventMulticaster類:

在觀察者模式中,一定要有一個管理維護監聽者清單的功能。在Spring的事件機制中,将維護監聽者清單的功能單獨定義了一個接口,即ApplicationEventMulticaster接口。這也展現了單一責任原則的設計思想。我們看其源碼:

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

}      

可以看到,這個接口定義了增删查監聽者的方法,是以,監聽者清單的維護通過這個接口實作。需要注意的是這個接口還定義了multicastEvent方法。通過這個方法,将事件傳給監聽器。是以這個類,将事件和監聽器,連接配接在一起。這裡采用的是中介者模式,這個接口就是中介者角色。

ApplicationEventPublisher:

Spring設計的事件釋出類,我們看其源碼:

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方法,進行事件的釋出。但是事件不是直接釋出到listener中,而是釋出在ApplicationEventMulticaster類中,是以在ApplicationEventPublisher類中,一定會有ApplicationEventMulticaster對象,将事件釋出到ApplicationEventMulticaster中。

事件流程總結:

通過上面幾個類的描述,我們總結一下spring事件機制的流程:

流程的核心,就是PublishEvent。Event對象以參數的形式傳入PublishEvent對象。然後将Event事件傳入ApplicationEventMulticaster類中,由ApplicationEventMulticaster類将事件傳給其維護的監聽者,執行監聽者方法。

領悟

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

應用

上面分析了Spring事件機制的運作原理,那麼對我們實際開發中,有何幫助呢?

這就需要我們結合源碼進行解讀了。

筆者找到一篇寫的很好的博文,大家可以參考:淺談Spring事件監聽。

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

@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("事件自定義方法");
    }
}      

這裡我們需要注意,有參構造中,傳入事件源,我們還可以在事件類中定義其他的方法,在監聽者中調用。因為監聽者監聽的是事件類,是以可以直接調到事件類的自定義方法。

下面,我們定義監聽者:

@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("監聽者發生一些改變");
        }
    }
}

      

需要注意的是,如果我們讓Spring自帶的事件釋出類去釋出事件,上面我們提到,會釋出到ApplicationEventMulticaster 中,而Spring預設的ApplicationEventMulticaster 類會把所有的事件都釋出給監聽者,它并不會去做過濾,什麼事件發給什麼監聽者。是以我們隻能在每個監聽者方法中去做判斷,事件類型屬于某個類型時,才做處理。

如果我們想特定的事件釋出給特定的監聽者,那我們隻能自己實作Spring的釋出類和ApplicationEventMulticaster類,自己定義事件的釋出機制。

更正:

ApplicationListener類是支援泛型的,在類後定義泛型,可以過濾掉其他的事件對象,隻接收泛型類事件。

最後,也是最關鍵的步驟,萬事俱備隻欠東風了,事件的釋出,是需要我們在業務代碼中自行進行釋出的,這裡我們用ApplicationContext作為釋出者,進行事件的釋出,代碼如下:

public class MyTest {

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

    }
}      

總結

Spring事件機制其實就是釋出-訂閱思想的一個展現,隻不過,Spring中的訂閱釋出隻限于在一個Spring容器中完成。而我們平時使用的訂閱釋出,都是分布式環境下的,一般采用MQ的方式完成。在實際的應用開發中,一個容器中進行訂閱釋出的需求并不是很多,當有些特殊需求可以轉化成容器内的訂閱釋出時,希望大家可以想到應用Spring提供的事件機制。

在單機環境中,我們要實作代碼的解耦,可以采用事件機制。例如:在前面講觀察者模式時,我們把監聽者的放在事件類中維護的方式,就是高耦合的。而Spring将其進行了解耦。

@Component
public class MyListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
      while (true){
          System.out.println("執行業務");
      }
    }
}