源碼級別的廣播與監聽實現

      網友投稿 719 2025-04-01

      近期疫情形勢嚴峻,情形不容樂觀,周末也不敢出去浪了,躲在家里“葛優躺”。閑來無事,又翻了遍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 { @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實戰方面的文章,感興趣的可以關注下,也可以來技術群討論問題呦!

      Spring

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:這個地方怎么取消 不要分段(地址怎么取消)
      下一篇:如何在Word中添加新詞以拼寫檢查字典?
      相關文章
      久久亚洲精品成人777大小说| 亚洲五月综合缴情在线观看| 黑人精品videos亚洲人| 亚洲成人一区二区| 亚洲AV无码乱码在线观看性色扶 | 亚洲色大成网站www尤物| 亚洲免费福利视频| 亚洲国产品综合人成综合网站| 亚洲日本在线观看网址| 亚洲天堂一区在线| 亚洲免费视频观看| jiz zz在亚洲| 亚洲精品无码高潮喷水A片软| 亚洲av最新在线观看网址| 亚洲大码熟女在线观看| 精品国产日韩亚洲一区在线| 麻豆亚洲AV成人无码久久精品| 国产成人久久精品亚洲小说| 亚洲第一成人影院| 国产性爱在线观看亚洲黄色一级片| 国产亚洲日韩一区二区三区| 亚洲乱码一区二区三区在线观看| 国产亚洲成AV人片在线观黄桃| 久久精品国产亚洲AV麻豆~| 久久亚洲日韩精品一区二区三区| 亚洲校园春色小说| 国产成人精品日本亚洲网址| 亚洲欧美黑人猛交群| 看亚洲a级一级毛片| 亚洲精品国产电影| 亚洲日韩av无码| 亚洲午夜未满十八勿入| 亚洲六月丁香六月婷婷色伊人| 亚洲午夜无码久久| 亚洲国产V高清在线观看| 亚洲精品午夜无码专区| 亚洲国产天堂久久综合网站 | 亚洲黄色一级毛片| 91丁香亚洲综合社区| 亚洲国产精华液2020| 亚洲国产精品成人|