Spring之事件機制詳解
機制詳解
Spring提供了事件機制,其本質是JDK提供的事件機制的應用,利用的是觀察者設計模式,具體請看設計模式之觀察者模式(Observer Pattern)。
這裡我們來分析Spring事件機制的原理。
先上UML圖,不熟悉UML規則的可以看UML類圖的制作規則。
下面我們對上圖中涉及到的幾個類進行講解。
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("執行業務");
}
}
}