AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)

來源: 責編: 時間:2023-11-01 09:18:58 309觀看
導讀1. 事件的層次傳播 在Spring中,ApplicationContext可以形成一個層次結(jié)構(gòu),通常由主容器和多個子容器組成。一個常見的疑問是:當一個事件在其中一個容器中發(fā)布時,這個事件會如何在這個層次結(jié)構(gòu)中傳播? 為了探討這個問題,我

1. 事件的層次傳播

 在Spring中,ApplicationContext可以形成一個層次結(jié)構(gòu),通常由主容器和多個子容器組成。一個常見的疑問是:當一個事件在其中一個容器中發(fā)布時,這個事件會如何在這個層次結(jié)構(gòu)中傳播?hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 為了探討這個問題,我們創(chuàng)建了一個名為HierarchicalEventPropagationEvent的事件類和一個對應(yīng)的監(jiān)聽器HierarchicalEventPropagationListener。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

全部代碼如下:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

package com.example.demo.event;import org.springframework.context.ApplicationEvent;// 事件類public class HierarchicalEventPropagationEvent extends ApplicationEvent {    private String message;    public HierarchicalEventPropagationEvent(Object source, String message) {        super(source);        this.message = message;    }    public String getMessage() {        return message;    }}

相應(yīng)地,為 HierarchicalEventPropagationEvent 定義一個監(jiān)聽器HierarchicalEventPropagationListener :hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

package com.example.demo.listener;import com.example.demo.event.HierarchicalEventPropagationEvent;import org.springframework.context.ApplicationListener;// 監(jiān)聽器類public class HierarchicalEventPropagationListener implements ApplicationListener<HierarchicalEventPropagationEvent> {    private String listenerId;    public HierarchicalEventPropagationListener(String listenerId) {        this.listenerId = listenerId;    }    @Override    public void onApplicationEvent(HierarchicalEventPropagationEvent event) {        System.out.println(listenerId + " received event - " + event.getMessage());    }}

 為了測試繼承機制,我們需要構(gòu)建主容器和子容器,并為每個容器注冊了一個監(jiān)聽器。初始化容器后,我們在兩個容器中分別發(fā)布事件。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 請注意,首先需要刷新主容器,然后刷新子容器。否則會出現(xiàn)異常:Exception in thread "main" java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: org.springframework.context.annotation.AnnotationConfigApplicationContext@somehashcodehr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

主程序如下:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

ackage com.example.demo;import com.example.demo.event.HierarchicalEventPropagationEvent;import com.example.demo.listener.HierarchicalEventPropagationListener;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class DemoApplication {    public static void main(String[] args) {        // 創(chuàng)建父容器,注冊監(jiān)聽器        AnnotationConfigApplicationContext parentCtx = new AnnotationConfigApplicationContext();        parentCtx.addApplicationListener(new HierarchicalEventPropagationListener("Parent Listener"));        parentCtx.refresh();        // 創(chuàng)建子容器,注冊監(jiān)聽器        AnnotationConfigApplicationContext childCtx = new AnnotationConfigApplicationContext();        childCtx.setParent(parentCtx);        childCtx.addApplicationListener(new HierarchicalEventPropagationListener("Child Listener"));        childCtx.refresh();        // 發(fā)布事件        HierarchicalEventPropagationEvent event1 = new HierarchicalEventPropagationEvent(parentCtx, "Event from parent");        parentCtx.publishEvent(event1);        HierarchicalEventPropagationEvent event2 = new HierarchicalEventPropagationEvent(childCtx, "Event from child");        childCtx.publishEvent(event2);    }}

運行結(jié)果

hr328資訊網(wǎng)——每日最新資訊28at.com


hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 主容器發(fā)布的事件只觸發(fā)了一次監(jiān)聽,而子容器發(fā)布的事件觸發(fā)了兩次監(jiān)聽。父容器和子容器都監(jiān)聽到了來自子容器的事件,而只有父容器監(jiān)聽到了來自父容器的事件。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 所以得出結(jié)論:在Spring的父子容器結(jié)構(gòu)中,事件會從子容器向上傳播至其父容器,但父容器中發(fā)布的事件不會向下傳播至子容器。 這種設(shè)計可以幫助開發(fā)者在父容器中集中處理所有的事件,而不必擔心事件在多個子容器之間的傳播。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

2. PayloadApplicationEvent的使用

 PayloadApplicationEvent是Spring提供的一種特殊事件,用于傳遞數(shù)據(jù)(稱為"payload")。所以不需要自定義事件,PayloadApplicationEvent可以直接傳遞任何類型的數(shù)據(jù),只需要指定它的類型即可。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

全部代碼如下:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

  • 定義監(jiān)聽器

首先,我們來看怎樣定義一個監(jiān)聽器來接收這個事件:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

通用監(jiān)聽器 - 會監(jiān)聽到所有種類的PayloadApplicationEvent:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

package com.example.demo.listener;import org.springframework.context.ApplicationListener;import org.springframework.context.PayloadApplicationEvent;/** * 通用監(jiān)聽器,能監(jiān)聽到所有類型的PayloadApplicationEvent */public class CustomObjectApplicationListener implements ApplicationListener<PayloadApplicationEvent> {        @Override    public void onApplicationEvent(PayloadApplicationEvent event) {        System.out.println("收到PayloadApplicationEvent,數(shù)據(jù)是:" + event.getPayload());    }}

特定數(shù)據(jù)類型的監(jiān)聽器 - 只會監(jiān)聽指定類型的數(shù)據(jù)。例如,如果我們只對字符串數(shù)據(jù)感興趣,我們可以如此定義:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

package com.example.demo.listener;import org.springframework.context.ApplicationListener;import org.springframework.context.PayloadApplicationEvent;/** * 特定數(shù)據(jù)類型的監(jiān)聽器。這個監(jiān)聽器專門監(jiān)聽String類型的PayloadApplicationEvent */public class CustomStringApplicationListener implements ApplicationListener<PayloadApplicationEvent<String>> {        @Override    public void onApplicationEvent(PayloadApplicationEvent<String> event) {        System.out.println("收到了字符串數(shù)據(jù):" + event.getPayload());    }}
  • 測試示例

要看這兩種監(jiān)聽器如何工作,我們來寫一個測試。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

package com.example.demo;import com.example.demo.listener.CustomObjectApplicationListener;import com.example.demo.listener.CustomStringApplicationListener;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.util.Date;public class DemoApplication {    public static void main(String[] args) {        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();        // 注冊監(jiān)聽器        ctx.addApplicationListener(new CustomObjectApplicationListener());        ctx.addApplicationListener(new CustomStringApplicationListener());        ctx.refresh();        // 發(fā)送事件        ctx.publishEvent("Hello, World!");  // 發(fā)送一個字符串        ctx.publishEvent(2023);              // 發(fā)送一個整數(shù)        ctx.publishEvent(new Date());        // 發(fā)送一個日期對象    }}

在這個測試中,我們發(fā)送了三種類型的數(shù)據(jù):一個字符串、一個整數(shù)和一個日期。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

執(zhí)行結(jié)果如下:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

從輸出可以看出:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

第一種監(jiān)聽器(通用的)接收到了所有三個事件,因為它不關(guān)心數(shù)據(jù)的具體類型。hr328資訊網(wǎng)——每日最新資訊28at.com

第二種監(jiān)聽器(字符串專用的)只接收到了字符串類型的事件。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

3. 為什么選擇自定義事件?

 雖然PayloadApplicationEvent提供了簡化事件監(jiān)聽的能力,但其可能不足以滿足特定的業(yè)務(wù)需求,尤其是當需要更多上下文和數(shù)據(jù)時。下面是一個使用自定義事件ArticlePublishedEvent的例子。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

全部代碼如下:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

自定義事件: ArticlePublishedEventhr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

這個事件代表了“新文章發(fā)布”,附帶有文章的標題、作者和發(fā)布日期等信息。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

package com.example.demo.event;import org.springframework.context.ApplicationEvent;public class ArticlePublishedEvent extends ApplicationEvent {    private String title;    private String author;    private String publishedDate;    public ArticlePublishedEvent(Object source, String title, String author, String publishedDate) {        super(source);        this.title = title;        this.author = author;        this.publishedDate = publishedDate;    }    public String getTitle() {        return title;    }    public String getAuthor() {        return author;    }    public String getPublishedDate() {        return publishedDate;    }}

自定義監(jiān)聽器: ArticlePublishedListener

hr328資訊網(wǎng)——每日最新資訊28at.com

這個監(jiān)聽器專門響應(yīng)ArticlePublishedEvent,執(zhí)行特定的業(yè)務(wù)邏輯,例如通知訂閱者、更新搜索索引等。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

import org.springframework.context.ApplicationListener;public class ArticlePublishedListener implements ApplicationListener<ArticlePublishedEvent> {    @Override    public void onApplicationEvent(ArticlePublishedEvent event) {        System.out.println("A new article has been published!");        System.out.println("Title: " + event.getTitle());        System.out.println("Author: " + event.getAuthor());        System.out.println("Published Date: " + event.getPublishedDate());        // Notify subscribers about the new article        notifySubscribers(event);        // Update search engine index with new article details        updateSearchIndex(event);        // Update statistical data about articles        updateStatistics(event);    }    private void notifySubscribers(ArticlePublishedEvent event) {        // Logic to notify subscribers (dummy logic for demonstration)        System.out.println("Notifying subscribers about the new article titled: " + event.getTitle());    }    private void updateSearchIndex(ArticlePublishedEvent event) {        // Logic to update search engine index (dummy logic for demonstration)        System.out.println("Updating search index with the new article titled: " + event.getTitle());    }    private void updateStatistics(ArticlePublishedEvent event) {        // Logic to update statistical data (dummy logic for demonstration)        System.out.println("Updating statistics with the new article titled: " + event.getTitle());    }}-----------------------------------?著作權(quán)歸作者所有:來自51CTO博客作者華為云開發(fā)者聯(lián)盟的原創(chuàng)作品,請聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責任掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)https://blog.51cto.com/u_15214399/8112689

在接收到新文章發(fā)布的事件后,監(jiān)聽器ArticlePublishedListener需要執(zhí)行以下業(yè)務(wù)邏輯:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

  • 通知所有訂閱新文章通知的用戶。
  • 將新文章的標題、作者和發(fā)布日期添加到搜索引擎的索引中,以便用戶可以搜索到這篇新文章。
  • 更新統(tǒng)計信息,例如總文章數(shù)、最近發(fā)布的文章等。

 這樣,每次新文章發(fā)布的事件被觸發(fā)時,訂閱者都會被通知,搜索引擎的索引將會得到更新,同時相關(guān)的統(tǒng)計數(shù)據(jù)也會得到更新。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

主程序:

hr328資訊網(wǎng)——每日最新資訊28at.com

模擬文章發(fā)布的場景hr328資訊網(wǎng)——每日最新資訊28at.com

package com.example.demo;import com.example.demo.event.ArticlePublishedEvent;import com.example.demo.listener.ArticlePublishedListener;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class DemoApplication {    public static void main(String[] args) {        // Initialize Spring ApplicationContext        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();        // Register listener        ctx.addApplicationListener(new ArticlePublishedListener());        ctx.refresh();        // Simulate publishing an article        ctx.publishEvent(new ArticlePublishedEvent(ctx, "Spring Events", "John Doe", "2023-09-15"));    }}-----------------------------------?著作權(quán)歸作者所有:來自51CTO博客作者華為云開發(fā)者聯(lián)盟的原創(chuàng)作品,請聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責任掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)https://blog.51cto.com/u_15214399/8112689

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

運行結(jié)果如下:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 我們可以看到ArticlePublishedEvent比PayloadApplicationEvent具有更多的業(yè)務(wù)含義和上下文。這樣的設(shè)計使我們能夠更具體地響應(yīng)和處理特定的業(yè)務(wù)事件。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 實際上,在企業(yè)級應(yīng)用中,文章發(fā)布可能會觸發(fā)多種不同的后續(xù)動作,使用Spring的事件監(jiān)聽器模式可以帶來如下優(yōu)勢:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

解耦:事件發(fā)布者(即新文章發(fā)布功能)不必關(guān)心具體的后續(xù)處理步驟。它只需發(fā)布事件,然后其他感興趣的監(jiān)聽器會相應(yīng)地做出響應(yīng)。這種設(shè)計有助于各個功能之間的解耦。hr328資訊網(wǎng)——每日最新資訊28at.com

可擴展性:如果未來需要為新文章發(fā)布添加更多的后續(xù)處理,只需添加更多的監(jiān)聽器即可,無需修改原有的業(yè)務(wù)邏輯。hr328資訊網(wǎng)——每日最新資訊28at.com

維護性:由于功能之間的解耦,每個功能模塊都可以獨立維護,這有助于提高代碼的可維護性。hr328資訊網(wǎng)——每日最新資訊28at.com

 Spring為開發(fā)者提供了強大的事件監(jiān)聽機制,無論是使用自定義事件還是利用PayloadApplicationEvent進行快速開發(fā),都使我們能夠構(gòu)建一個高度解耦、可擴展且易于維護的系統(tǒng)。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

4. 事件廣播原理

4.1 Spring 5.x的事件模型概述

核心概念:

ApplicationEvent:這是所有Spring事件的超類。用戶可以通過繼承此類來創(chuàng)建自定義事件。hr328資訊網(wǎng)——每日最新資訊28at.com

ApplicationListener:這是所有事件監(jiān)聽器的接口。它定義了一個onApplicationEvent方法,用于處理特定類型的事件。hr328資訊網(wǎng)——每日最新資訊28at.com

ApplicationEventPublisher:這是一個接口,定義了發(fā)布事件的方法。ApplicationContext繼承了這個接口,因此任何Spring bean都可以發(fā)布事件。hr328資訊網(wǎng)——每日最新資訊28at.com

ApplicationEventMulticaster:這個組件負責將事件廣播到所有匹配的監(jiān)聽器。hr328資訊網(wǎng)——每日最新資訊28at.com

事件發(fā)布:

 用戶可以通過ApplicationEventPublisher接口或ApplicationContext來發(fā)布事件。通常情況下,當我們在Spring bean中需要發(fā)布事件時,可以讓這個bean實現(xiàn)ApplicationEventPublisherAware接口,這樣Spring容器會注入一個事件發(fā)布器。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

異步事件:

 從Spring 4.2開始,我們可以輕松地使事件監(jiān)聽器異步化。在Spring 5中,這一功能仍然得到支持。只需要在監(jiān)聽器方法上添加@Async注解并確保啟用了異步支持。這使得事件處理可以在單獨的線程中執(zhí)行,不阻塞發(fā)布者。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

泛型事件:

 Spring 4.2引入了對泛型事件的支持,這在Spring 5中得到了維護。這意味著監(jiān)聽器現(xiàn)在可以根據(jù)事件的泛型類型進行過濾。例如,一個ApplicationListener<ApplicationEvent<String>>將只接收到攜帶String負載的事件。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

事件的排序:

 監(jiān)聽器可以實現(xiàn)Ordered接口或使用@Order注解來指定事件的執(zhí)行順序。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

新的事件類型:

 Spring 5引入了新的事件類型,如ServletRequestHandledEvent,為web請求處理提供更多的鉤子。而像ContextRefreshedEvent這樣的事件,雖然不是Spring 5新引入的,但它為特定的生命周期回調(diào)提供了鉤子。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

Reactive事件模型:

 與Spring 5引入的WebFlux一起,還引入了對反應(yīng)式編程模型的事件監(jiān)聽和發(fā)布的支持。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

總結(jié):

 在Spring 5.x中,事件模型得到了進一步的增強和優(yōu)化,增加了對異步、泛型和反應(yīng)式編程的支持,提供了更強大、靈活和高效的機制來處理應(yīng)用程序事件。對于開發(fā)者來說,這為在解耦的同時實現(xiàn)復雜的業(yè)務(wù)邏輯提供了便利。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

4.2 發(fā)布事件publishEvent源碼分析

上圖,這里是Spring 5.3.7的源碼,下面講單獨抽出來分析hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com


hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

public void publishEvent(ApplicationEvent event) {    this.publishEvent(event, (ResolvableType)null);}

分析:hr328資訊網(wǎng)——每日最新資訊28at.com

 該方法接受一個ApplicationEvent對象并調(diào)用其重載版本publishEvent(Object event, @Nullable ResolvableType eventType),為其傳遞null作為事件類型。這是為了簡化用戶使用,用戶可以直接傳遞一個ApplicationEvent對象而無需考慮其具體的類型。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

public void publishEvent(Object event) {    this.publishEvent(event, (ResolvableType)null);}

分析:hr328資訊網(wǎng)——每日最新資訊28at.com

 與上一個方法類似,但它接受任何Object作為事件,并將其與null的eventType一起傳遞給核心方法。這增加了靈活性,用戶可以發(fā)送任何對象作為事件,而不僅僅是ApplicationEvent對象。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {    // 檢查事件對象是否為空,確保發(fā)布的事件是有意義的    Assert.notNull(event, "Event must not be null");    // 判斷傳入的事件是否已經(jīng)是ApplicationEvent類型,如果是,則無需再進行包裝    Object applicationEvent;    if (event instanceof ApplicationEvent) {        applicationEvent = (ApplicationEvent)event;    } else {        // 如果傳入的事件不是ApplicationEvent類型,則將其包裝為PayloadApplicationEvent        applicationEvent = new PayloadApplicationEvent(this, event);        // 如果未指定事件類型,那么從包裝后的事件中獲取其真實類型        if (eventType == null) {            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();        }    }    // 判斷當前是否存在earlyApplicationEvents列表    if (this.earlyApplicationEvents != null) {        // 如果存在,說明ApplicationContext還未完全初始化,將事件添加到此列表中,稍后再進行處理        this.earlyApplicationEvents.add(applicationEvent);    } else {        // 如果ApplicationContext已經(jīng)初始化,那么直接通過事件多播器廣播事件        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);    }    // 如果存在父ApplicationContext,則也將事件發(fā)布到父容器中    if (this.parent != null) {        if (this.parent instanceof AbstractApplicationContext) {            // 如果父容器是AbstractApplicationContext類型,則帶上事件類型進行發(fā)布            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);        } else {            // 否則,只傳遞事件對象進行發(fā)布            this.parent.publishEvent(event);        }    }}-----------------------------------?著作權(quán)歸作者所有:來自51CTO博客作者華為云開發(fā)者聯(lián)盟的原創(chuàng)作品,請聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責任掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)https://blog.51cto.com/u_15214399/8112689

這個方法究竟做了什么?hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

事件非空檢查:為了確保事件對象不為空,進行了初步的斷言檢查。這是一個常見的做法,以防止無效的事件被廣播。hr328資訊網(wǎng)——每日最新資訊28at.com

事件類型檢查與封裝:Spring允許使用任意類型的對象作為事件。如果傳入的不是ApplicationEvent的實例,它會使用PayloadApplicationEvent來進行封裝。這種設(shè)計提供了更大的靈活性。hr328資訊網(wǎng)——每日最新資訊28at.com

早期事件的處理:在Spring的生命周期中,ApplicationContext可能還沒有完全初始化,這時會有一些早期的事件。如果earlyApplicationEvents不為空,這些事件會被添加到此列表中,稍后再廣播。hr328資訊網(wǎng)——每日最新資訊28at.com

事件廣播:如果ApplicationContext已初始化,事件會被廣播給所有的監(jiān)聽器。這是通過ApplicationEventMulticaster完成的,它是Spring中負責事件廣播的核心組件。hr328資訊網(wǎng)——每日最新資訊28at.com

處理父ApplicationContext:在有些應(yīng)用中,可以存在父子ApplicationContext。當子容器廣播一個事件時,也可以考慮在父容器中廣播這個事件。這是為了確保在整個上下文層次結(jié)構(gòu)中的所有感興趣的監(jiān)聽器都能收到事件。hr328資訊網(wǎng)——每日最新資訊28at.com

通過這種方式,Spring的事件發(fā)布機制確保了事件在不同的上下文和生命周期階段都能被正確處理和廣播。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

上面說到事件廣播是ApplicationEventMulticaster完成的,這個是什么?下面來看看hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

4.3 Spring事件廣播:從ApplicationEventMulticaster開始

 當我們在Spring中討論事件,我們實際上是在討論兩件事:事件(即發(fā)生的事情)和監(jiān)聽器(即對這些事件感興趣并作出反應(yīng)的實體)。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 ApplicationEventMulticaster的主要職責是管理事件監(jiān)聽器并廣播事件給這些監(jiān)聽器。我們主要關(guān)注SimpleApplicationEventMulticaster,因為這是默認的實現(xiàn),但請注意,Spring允許替換為自定義的ApplicationEventMulticaster。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

以下是SimpleApplicationEventMulticaster中的相關(guān)代碼與分析,有興趣的小伙伴可以自行查看:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {    // 可選的任務(wù)執(zhí)行器,用于異步調(diào)用事件監(jiān)聽器。    @Nullable    private Executor taskExecutor;    // 可選的錯誤處理器,用于處理在廣播事件過程中出現(xiàn)的錯誤。    @Nullable    private ErrorHandler errorHandler;    // 用于記錄日志的logger,它是延遲初始化的。    @Nullable    private volatile Log lazyLogger;    // 默認構(gòu)造函數(shù)。    public SimpleApplicationEventMulticaster() {    }    // 帶有BeanFactory參數(shù)的構(gòu)造函數(shù),通常用于更復雜的應(yīng)用上下文配置中。    public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {        this.setBeanFactory(beanFactory);    }    // 設(shè)置任務(wù)執(zhí)行器。可以是任何Java Executor,比如Spring的SimpleAsyncTaskExecutor或Java的FixedThreadPool。    public void setTaskExecutor(@Nullable Executor taskExecutor) {        this.taskExecutor = taskExecutor;    }    // 獲取當前設(shè)置的任務(wù)執(zhí)行器。    @Nullable    protected Executor getTaskExecutor() {        return this.taskExecutor;    }    // 設(shè)置錯誤處理器。    public void setErrorHandler(@Nullable ErrorHandler errorHandler) {        this.errorHandler = errorHandler;    }    // 獲取當前設(shè)置的錯誤處理器。    @Nullable    protected ErrorHandler getErrorHandler() {        return this.errorHandler;    }    // 這是廣播事件的主要方法。它首先解析事件的類型,然后調(diào)用具有額外參數(shù)的重載方法。    public void multicastEvent(ApplicationEvent event) {        this.multicastEvent(event, this.resolveDefaultEventType(event));    }    // 這個方法是真正執(zhí)行廣播操作的方法。    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {        // 確定事件類型。        ResolvableType type = (eventType != null) ? eventType : this.resolveDefaultEventType(event);        // 獲取任務(wù)執(zhí)行器。        Executor executor = this.getTaskExecutor();        // 獲取匹配此事件類型的所有監(jiān)聽器。        Iterator<ApplicationListener<?>> listeners = this.getApplicationListeners(event, type).iterator();        // 遍歷每個監(jiān)聽器并調(diào)用它。        while(listeners.hasNext()) {            ApplicationListener<?> listener = listeners.next();            // 如果有設(shè)置任務(wù)執(zhí)行器,則異步調(diào)用監(jiān)聽器。            if (executor != null) {                executor.execute(() -> this.invokeListener(listener, event));            } else {                // 如果沒有設(shè)置任務(wù)執(zhí)行器,則同步調(diào)用監(jiān)聽器。                this.invokeListener(listener, event);            }        }    }    // 為給定的事件解析默認的事件類型。    private ResolvableType resolveDefaultEventType(ApplicationEvent event) {        return ResolvableType.forInstance(event);    }    // 調(diào)用指定的監(jiān)聽器來處理給定的事件,并根據(jù)需要處理錯誤。    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {        // 獲取當前的錯誤處理器。        ErrorHandler errorHandler = this.getErrorHandler();        if (errorHandler != null) {            try {                // 嘗試調(diào)用監(jiān)聽器。                this.doInvokeListener(listener, event);            } catch (Throwable ex) {                // 如果出現(xiàn)錯誤,使用錯誤處理器處理。                errorHandler.handleError(ex);            }        } else {            // 如果沒有設(shè)置錯誤處理器,則直接調(diào)用監(jiān)聽器。            this.doInvokeListener(listener, event);        }    }    // 直接調(diào)用監(jiān)聽器,捕獲任何類型不匹配的異常。    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {        try {            listener.onApplicationEvent(event);        } catch (ClassCastException ex) {            // 捕獲類型轉(zhuǎn)換異常,并根據(jù)需要進行處理。            // 這可以確保如果監(jiān)聽器不能處理特定類型的事件,不會導致整個廣播操作失敗。            String msg = ex.getMessage();            if (msg != null && !this.matchesClassCastMessage(msg, event.getClass()) && (!(event instanceof PayloadApplicationEvent) || !this.matchesClassCastMessage(msg, ((PayloadApplicationEvent)event).getPayload().getClass()))) {                throw ex;            }                        // 在預(yù)期情況下捕獲并記錄異常,而不是拋出它。            Log loggerToUse = this.lazyLogger;            if (loggerToUse == null) {                loggerToUse = LogFactory.getLog(this.getClass());                this.lazyLogger = loggerToUse;            }            if (loggerToUse.isTraceEnabled()) {                loggerToUse.trace("Non-matching event type for listener: " + listener, ex);            }        }    }    // 根據(jù)給定的類型錯誤消息和事件類來檢查ClassCastException是否是預(yù)期的。    private boolean matchesClassCastMessage(String classCastMessage, Class<?> eventClass) {        if (classCastMessage.startsWith(eventClass.getName())) {            return true;        } else if (classCastMessage.startsWith(eventClass.toString())) {            return true;        } else {            int moduleSeparatorIndex = classCastMessage.indexOf(47);            return moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClass.getName(), moduleSeparatorIndex + 1);        }    }}

關(guān)于核心方法multicastEvent需要作出特別說明:hr328資訊網(wǎng)——每日最新資訊28at.com

// 這個方法是真正執(zhí)行廣播操作的方法。    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {        // 確定事件類型。        ResolvableType type = (eventType != null) ? eventType : this.resolveDefaultEventType(event);		......		// 獲取匹配此事件類型的所有監(jiān)聽器。        Iterator<ApplicationListener<?>> listeners = this.getApplicationListeners(event, type).iterator();        ......    }-----------------------------------?著作權(quán)歸作者所有:來自51CTO博客作者華為云開發(fā)者聯(lián)盟的原創(chuàng)作品,請聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責任掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)https://blog.51cto.com/u_15214399/8112689

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

方法第一行得到一個ResolvableType類型的對象,為什么 Spring 選擇使用 ResolvableType 而不是直接使用 Java 類型?最主要的原因是 Java 的泛型擦除。 在 Java 中,泛型只存在于編譯時,一旦代碼被編譯,泛型信息就會被擦除,運行時就不能直接獲取到泛型的實際類型。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

 為了解決這個問題,Spring 引入了 ResolvableType,一個能夠解析泛型類型信息的工具類。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

舉個例子:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

假設(shè)有如下的類定義:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

public class Sample {    private List<String> names;}

我們可以這樣獲取 names 字段的泛型類型:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

ResolvableType t = ResolvableType.forField(Sample.class.getDeclaredField("names"));Class<?> genericType = t.getGeneric(0).resolve(); // 得到 String.class

在 Spring 事件中的使用

hr328資訊網(wǎng)——每日最新資訊28at.com

 ResolvableType 在 Spring 事件中的應(yīng)用主要是確定事件的類型和監(jiān)聽器監(jiān)聽的事件類型。當我們發(fā)布一個事件:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

ApplicationEvent event = new MyCustomEvent(this, "data");applicationContext.publishEvent(event);

 Spring 內(nèi)部會使用 ResolvableType.forInstance(event) 來獲取這個事件的類型。然后,它會找到所有注冊的監(jiān)聽器,查看它們監(jiān)聽的事件類型是否與此事件匹配(通過比較 ResolvableType)。匹配的監(jiān)聽器會被調(diào)用。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

對于一個監(jiān)聽器:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

public class MyListener implements ApplicationListener<MyCustomEvent> {    @Override    public void onApplicationEvent(MyCustomEvent event) {        // handle the event    }}

Spring 內(nèi)部會使用 ResolvableType 來解析這個監(jiān)聽器監(jiān)聽的事件類型(在這個例子中是 MyCustomEvent)。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

總之,ResolvableType 在 Spring 中的主要用途是提供了一種方式來解析和操作運行時的泛型類型信息,特別是在事件發(fā)布和監(jiān)聽中。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

4.4 Spring事件發(fā)布與處理流程圖

如果看不清,建議在新標簽頁中打開圖片后放大看hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com


hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

4.5 監(jiān)聽器內(nèi)部邏輯

再來看看監(jiān)聽器內(nèi)部邏輯,我們來分析在multicastEvent方法中調(diào)用的getApplicationListeners(event, type)來分析下hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {    // 獲取事件來源對象    Object source = event.getSource();    // 判斷事件來源對象是否為null,是則返回null,否則返回事件來源對象的類    Class<?> sourceType = source != null ? source.getClass() : null;    // 使用事件類型和源類型作為緩存鍵    AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);    // 初始化一個新的監(jiān)聽器檢索器為null    AbstractApplicationEventMulticaster.CachedListenerRetriever newRetriever = null;    // 嘗試從緩存中使用鍵取得一個已存在的檢索器    AbstractApplicationEventMulticaster.CachedListenerRetriever existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);    // 如果沒有從緩存中獲取到檢索器,并且滿足緩存安全性條件    if (existingRetriever == null && (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {        // 創(chuàng)建一個新的檢索器        newRetriever = new AbstractApplicationEventMulticaster.CachedListenerRetriever();        // 嘗試將新檢索器添加到緩存中        existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);        // 如果緩存中已經(jīng)有了一個值(由于并發(fā)的原因),則將新檢索器設(shè)回null        if (existingRetriever != null) {            newRetriever = null;        }    }    // 如果有現(xiàn)有的檢索器    if (existingRetriever != null) {        // 嘗試從檢索器中獲取監(jiān)聽器集合        Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();        // 如果結(jié)果不為null,則直接返回        if (result != null) {            return result;        }    }    // 如果上述步驟都沒有返回,調(diào)用retrieveApplicationListeners進行實際的監(jiān)聽器檢索    return this.retrieveApplicationListeners(eventType, sourceType, newRetriever);}private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable AbstractApplicationEventMulticaster.CachedListenerRetriever retriever) {    // 初始化一個空的監(jiān)聽器列表    List<ApplicationListener<?>> allListeners = new ArrayList();    // 若retriever非null,則初始化集合來保存過濾出來的監(jiān)聽器和Bean名    Set<ApplicationListener<?>> filteredListeners = retriever != null ? new LinkedHashSet() : null;    Set<String> filteredListenerBeans = retriever != null ? new LinkedHashSet() : null;    LinkedHashSet listeners;    LinkedHashSet listenerBeans;    // 同步從defaultRetriever中獲取已注冊的監(jiān)聽器和其Bean名稱    synchronized(this.defaultRetriever) {        listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);        listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);    }    // 遍歷所有的監(jiān)聽器    for (ApplicationListener<?> listener : listeners) {        // 檢查該監(jiān)聽器是否支持此事件類型和源類型        if (this.supportsEvent(listener, eventType, sourceType)) {            if (retriever != null) {                // 如果支持并且retriever非null,添加到過濾監(jiān)聽器集合                filteredListeners.add(listener);            }            // 將支持的監(jiān)聽器添加到allListeners列表            allListeners.add(listener);        }    }    // 如果存在監(jiān)聽器Bean名稱    if (!listenerBeans.isEmpty()) {        ConfigurableBeanFactory beanFactory = this.getBeanFactory();        for (String listenerBeanName : listenerBeans) {            try {                // 檢查Bean工廠中的Bean是否支持該事件                if (this.supportsEvent(beanFactory, listenerBeanName, eventType)) {                    ApplicationListener<?> listener = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);                    // 再次檢查確保Bean實例支持事件,并且它還沒有被加入allListeners列表                    if (!allListeners.contains(listener) && this.supportsEvent(listener, eventType, sourceType)) {                        if (retriever != null) {                            // 若該Bean是單例并且retriever非null,添加到過濾監(jiān)聽器集合                            if (beanFactory.isSingleton(listenerBeanName)) {                                filteredListeners.add(listener);                            } else {                                filteredListenerBeans.add(listenerBeanName);                            }                        }                        // 添加到allListeners列表                        allListeners.add(listener);                    }                } else {                    // 若不支持該事件,從allListeners中移除該Bean                    Object listener = beanFactory.getSingleton(listenerBeanName);                    if (retriever != null) {                        filteredListeners.remove(listener);                    }                    allListeners.remove(listener);                }            } catch (NoSuchBeanDefinitionException e) {                // 若Bean不存在,直接繼續(xù)下一個            }        }    }    // 對allListeners列表進行排序,確保監(jiān)聽器的執(zhí)行順序    AnnotationAwareOrderComparator.sort(allListeners);    // 如果retriever非null,更新其內(nèi)部集合以后續(xù)使用    if (retriever != null) {        if (filteredListenerBeans.isEmpty()) {            retriever.applicationListeners = new LinkedHashSet(allListeners);            retriever.applicationListenerBeans = filteredListenerBeans;        } else {            retriever.applicationListeners = filteredListeners;            retriever.applicationListenerBeans = filteredListenerBeans;        }    }    // 返回allListeners作為結(jié)果    return allListeners;}-----------------------------------?著作權(quán)歸作者所有:來自51CTO博客作者華為云開發(fā)者聯(lián)盟的原創(chuàng)作品,請聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責任掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)https://blog.51cto.com/u_15214399/8112689

監(jiān)聽器內(nèi)部做了什么?hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

在getApplicationListeners方法中,采用了一種優(yōu)化檢索的緩存機制來提高性能并確保線程安全性。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

具體分析如下:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

首次檢查:

hr328資訊網(wǎng)——每日最新資訊28at.com

AbstractApplicationEventMulticaster.CachedListenerRetriever existingRetriever =     (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);

這里,我們首先從retrieverCache中檢索existingRetriever。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

判斷是否需要進入同步代碼塊:

if (existingRetriever == null && (this.beanClassLoader == null || ...)) {    ...}

hr328資訊網(wǎng)——每日最新資訊28at.com

如果existingRetriever為空,那么我們可能需要創(chuàng)建一個新的CachedListenerRetriever并放入緩存中。但是,為了確保線程安全性,我們必須在這之前進行進一步的檢查。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

雙重檢查:

在創(chuàng)建新的CachedListenerRetriever之前,我們使用了putIfAbsent方法。這個方法會嘗試添加一個新值,但如果該值已存在,它只會返回現(xiàn)有的值。該機制采用了一種緩存優(yōu)化策略:通過ConcurrentMap的putIfAbsent方法,即使多個線程同時到達這個代碼段,也確保只有一個線程能夠成功地放入新的值,從而保證線程安全性。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

newRetriever = new AbstractApplicationEventMulticaster.CachedListenerRetriever();existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);

這里的邏輯使用了ConcurrentMap的putIfAbsent方法來確保線程安全性,而沒有使用傳統(tǒng)的synchronized塊。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

所以,我們可以說getApplicationListeners中的這部分邏輯采用了一種優(yōu)化檢索的緩存機制。它利用了并發(fā)容器的原子性操作putIfAbsent來保證線程安全,而不是依賴于傳統(tǒng)的雙重檢查鎖定模式。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

總體概括一下,對于getApplicationListeners和retrieveApplicationListeners兩個方法的功能可以總結(jié)為以下三個步驟:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

從默認檢索器篩選監(jiān)聽器:

這部分代碼直接從defaultRetriever中獲取監(jiān)聽器,并檢查它們是否支持當前事件。在retrieveApplicationListeners方法中,代碼首先從defaultRetriever中獲取已經(jīng)編程式注入的監(jiān)聽器,并檢查每個監(jiān)聽器是否支持當前的事件類型。hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);for (ApplicationListener<?> listener : listeners) {    if (this.supportsEvent(listener, eventType, sourceType)) {        ... // 添加到篩選出來的監(jiān)聽器列表    }}

從IOC容器中篩選監(jiān)聽器:

在retrieveApplicationListeners方法中,除了從defaultRetriever中獲取已經(jīng)編程式注入的監(jiān)聽器,代碼還會嘗試從IOC容器(通過bean名稱)獲取監(jiān)聽器,并檢查它們是否支持當前的事件。hr328資訊網(wǎng)——每日最新資訊28at.com

if (!listenerBeans.isEmpty()) {    ConfigurableBeanFactory beanFactory = this.getBeanFactory();    for (String listenerBeanName : listenerBeans) {        ... // 檢查并添加到篩選出來的監(jiān)聽器列表    }}

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

監(jiān)聽器排序:

最后,為確保監(jiān)聽器按照預(yù)定的順序響應(yīng)事件,篩選出的所有監(jiān)聽器會經(jīng)過排序。排序基于Spring的@Order注解或Ordered接口,如AnnotationAwareOrderComparator.sort(allListeners)所示hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

AnnotationAwareOrderComparator.sort(allListeners);

4.6 Spring事件監(jiān)聽器檢索流程圖

hr328資訊網(wǎng)——每日最新資訊28at.com


hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

5. Spring事件傳播、異步處理等機制的詳細圖示

hr328資訊網(wǎng)——每日最新資訊28at.com


hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

說明:hr328資訊網(wǎng)——每日最新資訊28at.com

hr328資訊網(wǎng)——每日最新資訊28at.com

容器和事件廣播:

ApplicationContext 是Spring的應(yīng)用上下文容器。在圖中,我們有一個主容器和一個子容器。hr328資訊網(wǎng)——每日最新資訊28at.com

當我們想發(fā)布一個事件時,我們調(diào)用 publishEvent 方法。hr328資訊網(wǎng)——每日最新資訊28at.com

ApplicationEventMulticaster 負責實際地將事件廣播到各個監(jiān)聽器。hr328資訊網(wǎng)——每日最新資訊28at.com

主容器和子容器關(guān)系:

在Spring中,可以有多個容器,其中一個是主容器,其他的則是子容器。hr328資訊網(wǎng)——每日最新資訊28at.com

通常,子容器可以訪問主容器中的bean,但反之則不行。但在事件傳播的上下文中,子容器發(fā)布的事件默認不會在主容器中傳播。這一點由 Note1 注釋標明。hr328資訊網(wǎng)——每日最新資訊28at.com

異步處理:

當事件被發(fā)布時,它可以被異步地傳播到監(jiān)聽器,這取決于是否配置了異步執(zhí)行器。hr328資訊網(wǎng)——每日最新資訊28at.com

是否使用異步執(zhí)行器? 這個決策點說明了基于配置,事件可以同步或異步地傳播到監(jiān)聽器。hr328資訊網(wǎng)——每日最新資訊28at.com

事件生命周期:

在Spring容器的生命周期中,有些事件在容器初始化前觸發(fā),這些被稱為 early events。這些事件會被緩存起來,直到容器初始化完成。hr328資訊網(wǎng)——每日最新資訊28at.com

一旦容器初始化完成,這些早期的事件會被處理,并開始處理常規(guī)事件。hr328資訊網(wǎng)——每日最新資訊28at.com

在容器銷毀時,也可能觸發(fā)事件。hr328資訊網(wǎng)——每日最新資訊28at.com

注意事項 (Note1):hr328資訊網(wǎng)——每日最新資訊28at.com

這個部分強調(diào)了一個特定的行為,即在某些配置下,子容器發(fā)布的事件可能也會在主容器中傳播,但這并不是默認行為。hr328資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-16286-0.html掌握Spring事件監(jiān)聽器的內(nèi)部邏輯與實現(xiàn)

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 深入剖析Netflix Ribbon:分布式微服務(wù)架構(gòu)的負載均衡神器

下一篇: WebGPU 入門:繪制一個三角形

標簽:
  • 熱門焦點
Top