Spring之事件機(jī)制詳解
機(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ī)器人 記得一定要等待,等待才有好東西
