原創:微信公眾號 【阿Q說代碼】,歡迎分享,轉載請保留出處,
近期疫情形勢嚴峻,情形不容樂觀,周末也不敢出去浪了,躲在家里“葛優躺”,閑來無事,又翻了遍Spring的原始碼,不翻不知道,一翻嚇一跳,之前翻過的原始碼已經吃進了肚子里,再見亦是陌生人,
個人建議:為了以后能快速的撿起某個知識點,最好的方法還是形成檔案,下次有遺漏的時候,直接讀檔案,按之前的思路捋一遍,“干凈又衛生”,
之前的文章中我們已經介紹過如何在專案中快速上手“事件通知機制”,相信大家已經掌握了,但是我們作為高級javaer,要知其然,更要知其所以然,今天就帶大家從原始碼的角度來分析一下廣播與監聽的底層實作原理,
原始碼匯入教程也給你準備好了,不來試試嗎?
版本號:spring-framework-5.0.x
原始碼決議
為了實作廣播與監聽的功能,Spring為我們提供了兩個重要的函式式介面:ApplicationEventPublisher和ApplicationListener,前者的publishEvent()方法為我們提供了發送廣播的能力;后者的onApplicationEvent()方法為我們提供了監聽并處理事件的能力,
接下來我們就來分析一下spring是如何運用這兩種能力的,
不知道大家對單例物件的初始化呼叫程序是否熟悉?主要呼叫方法流程如下:

發送廣播
applyBeanPostProcessorsBeforeInitialization方法會去遍歷該工廠創建的所有的Bean后置處理器,然后去依次執行后置處理器對應的postProcessBeforeInitialization方法,
在該方法的實作類中我們看到了兩個熟悉的類名

不知道大家還記得不,這倆類是在beanFactory的準備作業程序中添加的兩個bean的后置處理器,所以這個地方會依次去執行這兩個類中的實作方法,

由于藍框中類的實作方法是默認實作按照原樣回傳的給定的bean,所以此處不用過多分析,我們重點來看下紅框中類的方法實作,
該方法中最重要的是invokeAwareInterfaces方法,它的作用是檢測對應的bean是否實作了某個Aware介面,如果實作了的話就去進行相關的呼叫,
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
我們發現在invokeAwareInterfaces方法中出現了如上代碼,這不就是和廣播發送相關的嗎?所以只要我們寫一個類來實作ApplicationEventPublisherAware介面,就可以在該bean中注入一個ApplicationEventPublisher物件,也就獲得了發送廣播的能力,
監聽訊息
applyBeanPostProcessorsAfterInitialization方法也會去遍歷該工廠創建的所有的Bean后置處理器,然后去依次執行后置處理器對應的postProcessAfterInitialization方法,
同樣的,該方法的實作類中也有ApplicationContextAwareProcessor和ApplicationListenerDetector兩個類,但是不同的是,前者的類的實作方法是默認實作按照原樣回傳的給定bean,而后者做了相關的處理,
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
上述代碼是將實作了ApplicationListener介面的bean添加到監聽器串列中,最終是保存在AbstractApplicationEventMulticaster的成員變數defaultRetriever的集合applicationListeners中,
猜想:當發送廣播訊息時,就直接找到集合中的這些監聽器,然后呼叫每個監聽器的
onApplicationEvent方法完成事件的處理,
案例分析
在refresh()的finishRefresh()方法中,
publishEvent(new ContextRefreshedEvent(this));
發送一條事件型別為ContextRefreshedEvent的廣播訊息,用來代表Spring容器初始化結束,通過分析發現,該方法中最主要的就是如下代碼:
//真正的廣播交給 applicationEventMulticaster 來完成
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
refresh()的initApplicationEventMulticaster()將applicationEventMulticaster初始化為SimpleApplicationEventMulticaster
在實作類SimpleApplicationEventMulticaster的方法中,會找到已注冊的ApplicationListener串列,然后分別呼叫invokeListener方法(將監聽和事件作為引數傳到方法并執行的程序就是發送廣播的程序),
底層呼叫的是listener.onApplicationEvent(event);方法,也就是各個監聽實作類單獨處理廣播訊息的邏輯,
訊息與監聽系結
看到這兒,你是不是已經發現了:訊息型別和監聽器的系結發生在廣播程序中,接下來就讓我們去一探究竟
我們看一下multicastEvent()方法中的getApplicationListeners(event, type)方法,
在該方法中,用到了ConcurrentHashMap型別的快取retrieverCache,所以每種型別的事件在廣播的時候會觸發一次系結操作,它的key由事件的來源和型別確定,它的value中就包含了由事件來源和型別所確定的所有監聽串列,
其中系結的邏輯就出現在retrieveApplicationListeners方法中,大家可以去原始碼中查看,
實戰教學
紙上得來終覺淺,絕知此事要躬行,為了更好地理解廣播與監聽的流程,我們當然得用實戰來加以輔佐!
自定義事件
public class MyEvent extends ApplicationContextEvent {
public MyEvent(ApplicationContext source) {
super(source);
}
}
自定義廣播
@Component
public class MyPublisher implements ApplicationEventPublisherAware, ApplicationContextAware {
private ApplicationEventPublisher applicationEventPublisher;
private ApplicationContext applicationContext;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//發送廣播訊息
public void publishEvent(){
System.out.println("我要開始發送訊息了,,,");
MyEvent myEvent = new MyEvent(applicationContext);
applicationEventPublisher.publishEvent(myEvent);
}
}
MyPublisher實作了ApplicationEventPublisherAware介面 ,在spring初始化(見上文中的invokeAwareInterfaces)的時候會回呼setApplicationEventPublisher方法,獲取到初始化(添加bean后置處理器ApplicationContextAwareProcessor)時的AbstractApplicationContext,而AbstractApplicationContext又間接實作了ApplicationEventPublisher而獲得發送能力,真正執行的是 AbstractApplicationContext 類中的 publishEvent 方法,
自定義監聽
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
System.out.println("我監聽到你的訊息了");
}
}
MyEventListener實作了ApplicationListener介面,在spring初始化(見上文中的addApplicationListener)的時候會添加到applicationListeners中,在執行publishEvent 方法時就會走MyEventListener中的onApplicationEvent方法,
客戶端
@RestController
@RequestMapping("/demo")
public class DemoTest {
@Autowired
private MyPublisher myPublisher;
@RequestMapping("/test")
public void test() {
myPublisher.publishEvent();
}
}
訪問127.0.0.1:8008/demo/test就可以發送廣播了,發送與監聽內容如下:
我要開始發送訊息了,,,
我監聽到你的訊息了
看到這兒,相信你己經完全掌握了廣播與監聽的精髓了,趕快實踐起來吧,阿Q將持續更新java實戰方面的文章,感興趣的可以關注下,也可以來技術群討論問題呦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/457541.html
標籤:其他
上一篇:【Python】破解摩斯密碼
