源码级别的广播与监听实现
作者 |?阿Q
來源 |?阿Q說代碼
閑來無事,又翻了遍Spring的源碼。不翻不知道,一翻嚇一跳,之前翻過的源碼已經吃進了肚子里,再見亦是陌生人。
今天就帶大家從源碼的角度來分析一下廣播與監聽的底層實現原理。
源碼解析
為了實現廣播與監聽的功能,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;@Overridepublic?void?setApplicationEventPublisher(ApplicationEventPublisher?applicationEventPublisher)?{this.applicationEventPublisher?=?applicationEventPublisher;}@Overridepublic?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>?{@Overridepublic?void?onApplicationEvent(MyEvent?event)?{System.out.println("我監聽到你的消息了");} }MyEventListener實現了ApplicationListener接口,在spring初始化(見上文中的addApplicationListener)的時候會添加到applicationListeners中,在執行publishEvent 方法時就會走MyEventListener中的onApplicationEvent方法。
客戶端
@RestController @RequestMapping("/demo") public?class?DemoTest?{@Autowiredprivate?MyPublisher?myPublisher;@RequestMapping("/test")public?void?test()?{myPublisher.publishEvent();} }訪問127.0.0.1:8008/demo/test就可以發送廣播了,發送與監聽內容如下:
我要開始發送消息了。。。 我監聽到你的消息了看到這兒,相信你己經完全掌握了廣播與監聽的精髓了,趕快實踐起來吧。
往期推薦
如果讓你來設計網絡
用過留痕,誰動了我的檔案?
一把王者的時間,我就學會了Nginx
如何在 Kubernetes Pod 內進行網絡抓包
點分享
點收藏
點點贊
點在看
總結
以上是生活随笔為你收集整理的源码级别的广播与监听实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: stack vs heap:栈区分配内存
- 下一篇: 赋能开发者,英特尔发布oneAPI 20