掌握Spring事件監(jiān)聽(tīng)器的內(nèi)部邏輯與實(shí)現(xiàn)
1. 事件的層次傳播
在Spring中,ApplicationContext可以形成一個(gè)層次結(jié)構(gòu),通常由主容器和多個(gè)子容器組成。一個(gè)常見(jiàn)的疑問(wèn)是:當(dāng)一個(gè)事件在其中一個(gè)容器中發(fā)布時(shí),這個(gè)事件會(huì)如何在這個(gè)層次結(jié)構(gòu)中傳播?
為了探討這個(gè)問(wèn)題,我們創(chuàng)建了一個(gè)名為HierarchicalEventPropagationEvent的事件類(lèi)和一個(gè)對(duì)應(yīng)的監(jiān)聽(tīng)器HierarchicalEventPropagationListener。
全部代碼如下:
package com.example.demo.event;
import org.springframework.context.ApplicationEvent;
// 事件類(lèi)
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 定義一個(gè)監(jiān)聽(tīng)器HierarchicalEventPropagationListener :
package com.example.demo.listener;
import com.example.demo.event.HierarchicalEventPropagationEvent;
import org.springframework.context.ApplicationListener;
// 監(jiān)聽(tīng)器類(lèi)
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());
}
}為了測(cè)試?yán)^承機(jī)制,我們需要構(gòu)建主容器和子容器,并為每個(gè)容器注冊(cè)了一個(gè)監(jiān)聽(tīng)器。初始化容器后,我們?cè)趦蓚€(gè)容器中分別發(fā)布事件。
請(qǐng)注意,首先需要刷新主容器,然后刷新子容器。否則會(huì)出現(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@somehashcode
主程序如下:
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)建父容器,注冊(cè)監(jiān)聽(tīng)器
AnnotationConfigApplicationContext parentCtx = new AnnotationConfigApplicationContext();
parentCtx.addApplicationListener(new HierarchicalEventPropagationListener("Parent Listener"));
parentCtx.refresh();
// 創(chuàng)建子容器,注冊(cè)監(jiān)聽(tīng)器
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);
}
}運(yùn)行結(jié)果

主容器發(fā)布的事件只觸發(fā)了一次監(jiān)聽(tīng),而子容器發(fā)布的事件觸發(fā)了兩次監(jiān)聽(tīng)。父容器和子容器都監(jiān)聽(tīng)到了來(lái)自子容器的事件,而只有父容器監(jiān)聽(tīng)到了來(lái)自父容器的事件。
所以得出結(jié)論:在Spring的父子容器結(jié)構(gòu)中,事件會(huì)從子容器向上傳播至其父容器,但父容器中發(fā)布的事件不會(huì)向下傳播至子容器。 這種設(shè)計(jì)可以幫助開(kāi)發(fā)者在父容器中集中處理所有的事件,而不必?fù)?dān)心事件在多個(gè)子容器之間的傳播。
2. PayloadApplicationEvent的使用
PayloadApplicationEvent是Spring提供的一種特殊事件,用于傳遞數(shù)據(jù)(稱(chēng)為"payload")。所以不需要自定義事件,PayloadApplicationEvent可以直接傳遞任何類(lèi)型的數(shù)據(jù),只需要指定它的類(lèi)型即可。
全部代碼如下:
- 定義監(jiān)聽(tīng)器
首先,我們來(lái)看怎樣定義一個(gè)監(jiān)聽(tīng)器來(lái)接收這個(gè)事件:
通用監(jiān)聽(tīng)器 - 會(huì)監(jiān)聽(tīng)到所有種類(lèi)的PayloadApplicationEvent:
package com.example.demo.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.context.PayloadApplicationEvent;
/**
* 通用監(jiān)聽(tīng)器,能監(jiān)聽(tīng)到所有類(lèi)型的PayloadApplicationEvent
*/
public class CustomObjectApplicationListener implements ApplicationListener<PayloadApplicationEvent> {
@Override
public void onApplicationEvent(PayloadApplicationEvent event) {
System.out.println("收到PayloadApplicationEvent,數(shù)據(jù)是:" + event.getPayload());
}
}
特定數(shù)據(jù)類(lèi)型的監(jiān)聽(tīng)器 - 只會(huì)監(jiān)聽(tīng)指定類(lèi)型的數(shù)據(jù)。例如,如果我們只對(duì)字符串?dāng)?shù)據(jù)感興趣,我們可以如此定義:
package com.example.demo.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.context.PayloadApplicationEvent;
/**
* 特定數(shù)據(jù)類(lèi)型的監(jiān)聽(tīng)器。這個(gè)監(jiān)聽(tīng)器專(zhuān)門(mén)監(jiān)聽(tīng)String類(lèi)型的PayloadApplicationEvent
*/
public class CustomStringApplicationListener implements ApplicationListener<PayloadApplicationEvent<String>> {
@Override
public void onApplicationEvent(PayloadApplicationEvent<String> event) {
System.out.println("收到了字符串?dāng)?shù)據(jù):" + event.getPayload());
}
}- 測(cè)試示例
要看這兩種監(jiān)聽(tīng)器如何工作,我們來(lái)寫(xiě)一個(gè)測(cè)試。
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();
// 注冊(cè)監(jiān)聽(tīng)器
ctx.addApplicationListener(new CustomObjectApplicationListener());
ctx.addApplicationListener(new CustomStringApplicationListener());
ctx.refresh();
// 發(fā)送事件
ctx.publishEvent("Hello, World!"); // 發(fā)送一個(gè)字符串
ctx.publishEvent(2023); // 發(fā)送一個(gè)整數(shù)
ctx.publishEvent(new Date()); // 發(fā)送一個(gè)日期對(duì)象
}
}在這個(gè)測(cè)試中,我們發(fā)送了三種類(lèi)型的數(shù)據(jù):一個(gè)字符串、一個(gè)整數(shù)和一個(gè)日期。
執(zhí)行結(jié)果如下:

從輸出可以看出:
第一種監(jiān)聽(tīng)器(通用的)接收到了所有三個(gè)事件,因?yàn)樗魂P(guān)心數(shù)據(jù)的具體類(lèi)型。
第二種監(jiān)聽(tīng)器(字符串專(zhuān)用的)只接收到了字符串類(lèi)型的事件。
3. 為什么選擇自定義事件?
雖然PayloadApplicationEvent提供了簡(jiǎn)化事件監(jiān)聽(tīng)的能力,但其可能不足以滿(mǎn)足特定的業(yè)務(wù)需求,尤其是當(dāng)需要更多上下文和數(shù)據(jù)時(shí)。下面是一個(gè)使用自定義事件ArticlePublishedEvent的例子。
全部代碼如下:
自定義事件: ArticlePublishedEvent
這個(gè)事件代表了“新文章發(fā)布”,附帶有文章的標(biāo)題、作者和發(fā)布日期等信息。
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)聽(tīng)器: ArticlePublishedListener
這個(gè)監(jiān)聽(tīng)器專(zhuān)門(mén)響應(yīng)ArticlePublishedEvent,執(zhí)行特定的業(yè)務(wù)邏輯,例如通知訂閱者、更新搜索索引等。
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)歸作者所有:來(lái)自51CTO博客作者華為云開(kāi)發(fā)者聯(lián)盟的原創(chuàng)作品,請(qǐng)聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責(zé)任
掌握Spring事件監(jiān)聽(tīng)器的內(nèi)部邏輯與實(shí)現(xiàn)
https://blog.51cto.com/u_15214399/8112689在接收到新文章發(fā)布的事件后,監(jiān)聽(tīng)器ArticlePublishedListener需要執(zhí)行以下業(yè)務(wù)邏輯:
- 通知所有訂閱新文章通知的用戶(hù)。
- 將新文章的標(biāo)題、作者和發(fā)布日期添加到搜索引擎的索引中,以便用戶(hù)可以搜索到這篇新文章。
- 更新統(tǒng)計(jì)信息,例如總文章數(shù)、最近發(fā)布的文章等。
這樣,每次新文章發(fā)布的事件被觸發(fā)時(shí),訂閱者都會(huì)被通知,搜索引擎的索引將會(huì)得到更新,同時(shí)相關(guān)的統(tǒng)計(jì)數(shù)據(jù)也會(huì)得到更新。
主程序:
模擬文章發(fā)布的場(chǎng)景
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)歸作者所有:來(lái)自51CTO博客作者華為云開(kāi)發(fā)者聯(lián)盟的原創(chuàng)作品,請(qǐng)聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責(zé)任
掌握Spring事件監(jiān)聽(tīng)器的內(nèi)部邏輯與實(shí)現(xiàn)
https://blog.51cto.com/u_15214399/8112689
運(yùn)行結(jié)果如下:

我們可以看到ArticlePublishedEvent比PayloadApplicationEvent具有更多的業(yè)務(wù)含義和上下文。這樣的設(shè)計(jì)使我們能夠更具體地響應(yīng)和處理特定的業(yè)務(wù)事件。
實(shí)際上,在企業(yè)級(jí)應(yīng)用中,文章發(fā)布可能會(huì)觸發(fā)多種不同的后續(xù)動(dòng)作,使用Spring的事件監(jiān)聽(tīng)器模式可以帶來(lái)如下優(yōu)勢(shì):
解耦:事件發(fā)布者(即新文章發(fā)布功能)不必關(guān)心具體的后續(xù)處理步驟。它只需發(fā)布事件,然后其他感興趣的監(jiān)聽(tīng)器會(huì)相應(yīng)地做出響應(yīng)。這種設(shè)計(jì)有助于各個(gè)功能之間的解耦。
可擴(kuò)展性:如果未來(lái)需要為新文章發(fā)布添加更多的后續(xù)處理,只需添加更多的監(jiān)聽(tīng)器即可,無(wú)需修改原有的業(yè)務(wù)邏輯。
維護(hù)性:由于功能之間的解耦,每個(gè)功能模塊都可以獨(dú)立維護(hù),這有助于提高代碼的可維護(hù)性。
Spring為開(kāi)發(fā)者提供了強(qiáng)大的事件監(jiān)聽(tīng)機(jī)制,無(wú)論是使用自定義事件還是利用PayloadApplicationEvent進(jìn)行快速開(kāi)發(fā),都使我們能夠構(gòu)建一個(gè)高度解耦、可擴(kuò)展且易于維護(hù)的系統(tǒng)。
4. 事件廣播原理
4.1 Spring 5.x的事件模型概述
核心概念:
ApplicationEvent:這是所有Spring事件的超類(lèi)。用戶(hù)可以通過(guò)繼承此類(lèi)來(lái)創(chuàng)建自定義事件。
ApplicationListener:這是所有事件監(jiān)聽(tīng)器的接口。它定義了一個(gè)onApplicationEvent方法,用于處理特定類(lèi)型的事件。
ApplicationEventPublisher:這是一個(gè)接口,定義了發(fā)布事件的方法。ApplicationContext繼承了這個(gè)接口,因此任何Spring bean都可以發(fā)布事件。
ApplicationEventMulticaster:這個(gè)組件負(fù)責(zé)將事件廣播到所有匹配的監(jiān)聽(tīng)器。
事件發(fā)布:
用戶(hù)可以通過(guò)ApplicationEventPublisher接口或ApplicationContext來(lái)發(fā)布事件。通常情況下,當(dāng)我們?cè)赟pring bean中需要發(fā)布事件時(shí),可以讓這個(gè)bean實(shí)現(xiàn)ApplicationEventPublisherAware接口,這樣Spring容器會(huì)注入一個(gè)事件發(fā)布器。
異步事件:
從Spring 4.2開(kāi)始,我們可以輕松地使事件監(jiān)聽(tīng)器異步化。在Spring 5中,這一功能仍然得到支持。只需要在監(jiān)聽(tīng)器方法上添加@Async注解并確保啟用了異步支持。這使得事件處理可以在單獨(dú)的線(xiàn)程中執(zhí)行,不阻塞發(fā)布者。
泛型事件:
Spring 4.2引入了對(duì)泛型事件的支持,這在Spring 5中得到了維護(hù)。這意味著監(jiān)聽(tīng)器現(xiàn)在可以根據(jù)事件的泛型類(lèi)型進(jìn)行過(guò)濾。例如,一個(gè)ApplicationListener<ApplicationEvent<String>>將只接收到攜帶String負(fù)載的事件。
事件的排序:
監(jiān)聽(tīng)器可以實(shí)現(xiàn)Ordered接口或使用@Order注解來(lái)指定事件的執(zhí)行順序。
新的事件類(lèi)型:
Spring 5引入了新的事件類(lèi)型,如ServletRequestHandledEvent,為web請(qǐng)求處理提供更多的鉤子。而像ContextRefreshedEvent這樣的事件,雖然不是Spring 5新引入的,但它為特定的生命周期回調(diào)提供了鉤子。
Reactive事件模型:
與Spring 5引入的WebFlux一起,還引入了對(duì)反應(yīng)式編程模型的事件監(jiān)聽(tīng)和發(fā)布的支持。
總結(jié):
在Spring 5.x中,事件模型得到了進(jìn)一步的增強(qiáng)和優(yōu)化,增加了對(duì)異步、泛型和反應(yīng)式編程的支持,提供了更強(qiáng)大、靈活和高效的機(jī)制來(lái)處理應(yīng)用程序事件。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),這為在解耦的同時(shí)實(shí)現(xiàn)復(fù)雜的業(yè)務(wù)邏輯提供了便利。
4.2 發(fā)布事件publishEvent源碼分析
上圖,這里是Spring 5.3.7的源碼,下面講單獨(dú)抽出來(lái)分析

public void publishEvent(ApplicationEvent event) {
this.publishEvent(event, (ResolvableType)null);
}分析:
該方法接受一個(gè)ApplicationEvent對(duì)象并調(diào)用其重載版本publishEvent(Object event, @Nullable ResolvableType eventType),為其傳遞null作為事件類(lèi)型。這是為了簡(jiǎn)化用戶(hù)使用,用戶(hù)可以直接傳遞一個(gè)ApplicationEvent對(duì)象而無(wú)需考慮其具體的類(lèi)型。
public void publishEvent(Object event) {
this.publishEvent(event, (ResolvableType)null);
}分析:
與上一個(gè)方法類(lèi)似,但它接受任何Object作為事件,并將其與null的eventType一起傳遞給核心方法。這增加了靈活性,用戶(hù)可以發(fā)送任何對(duì)象作為事件,而不僅僅是ApplicationEvent對(duì)象。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
// 檢查事件對(duì)象是否為空,確保發(fā)布的事件是有意義的
Assert.notNull(event, "Event must not be null");
// 判斷傳入的事件是否已經(jīng)是ApplicationEvent類(lèi)型,如果是,則無(wú)需再進(jìn)行包裝
Object applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
// 如果傳入的事件不是ApplicationEvent類(lèi)型,則將其包裝為PayloadApplicationEvent
applicationEvent = new PayloadApplicationEvent(this, event);
// 如果未指定事件類(lèi)型,那么從包裝后的事件中獲取其真實(shí)類(lèi)型
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
// 判斷當(dāng)前是否存在earlyApplicationEvents列表
if (this.earlyApplicationEvents != null) {
// 如果存在,說(shuō)明ApplicationContext還未完全初始化,將事件添加到此列表中,稍后再進(jìn)行處理
this.earlyApplicationEvents.add(applicationEvent);
} else {
// 如果ApplicationContext已經(jīng)初始化,那么直接通過(guò)事件多播器廣播事件
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
// 如果存在父ApplicationContext,則也將事件發(fā)布到父容器中
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
// 如果父容器是AbstractApplicationContext類(lèi)型,則帶上事件類(lèi)型進(jìn)行發(fā)布
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
// 否則,只傳遞事件對(duì)象進(jìn)行發(fā)布
this.parent.publishEvent(event);
}
}
}
-----------------------------------
?著作權(quán)歸作者所有:來(lái)自51CTO博客作者華為云開(kāi)發(fā)者聯(lián)盟的原創(chuàng)作品,請(qǐng)聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責(zé)任
掌握Spring事件監(jiān)聽(tīng)器的內(nèi)部邏輯與實(shí)現(xiàn)
https://blog.51cto.com/u_15214399/8112689這個(gè)方法究竟做了什么?
事件非空檢查:為了確保事件對(duì)象不為空,進(jìn)行了初步的斷言檢查。這是一個(gè)常見(jiàn)的做法,以防止無(wú)效的事件被廣播。
事件類(lèi)型檢查與封裝:Spring允許使用任意類(lèi)型的對(duì)象作為事件。如果傳入的不是ApplicationEvent的實(shí)例,它會(huì)使用PayloadApplicationEvent來(lái)進(jìn)行封裝。這種設(shè)計(jì)提供了更大的靈活性。
早期事件的處理:在Spring的生命周期中,ApplicationContext可能還沒(méi)有完全初始化,這時(shí)會(huì)有一些早期的事件。如果earlyApplicationEvents不為空,這些事件會(huì)被添加到此列表中,稍后再?gòu)V播。
事件廣播:如果ApplicationContext已初始化,事件會(huì)被廣播給所有的監(jiān)聽(tīng)器。這是通過(guò)ApplicationEventMulticaster完成的,它是Spring中負(fù)責(zé)事件廣播的核心組件。
處理父ApplicationContext:在有些應(yīng)用中,可以存在父子ApplicationContext。當(dāng)子容器廣播一個(gè)事件時(shí),也可以考慮在父容器中廣播這個(gè)事件。這是為了確保在整個(gè)上下文層次結(jié)構(gòu)中的所有感興趣的監(jiān)聽(tīng)器都能收到事件。
通過(guò)這種方式,Spring的事件發(fā)布機(jī)制確保了事件在不同的上下文和生命周期階段都能被正確處理和廣播。
上面說(shuō)到事件廣播是ApplicationEventMulticaster完成的,這個(gè)是什么?下面來(lái)看看
4.3 Spring事件廣播:從ApplicationEventMulticaster開(kāi)始
當(dāng)我們?cè)赟pring中討論事件,我們實(shí)際上是在討論兩件事:事件(即發(fā)生的事情)和監(jiān)聽(tīng)器(即對(duì)這些事件感興趣并作出反應(yīng)的實(shí)體)。
ApplicationEventMulticaster的主要職責(zé)是管理事件監(jiān)聽(tīng)器并廣播事件給這些監(jiān)聽(tīng)器。我們主要關(guān)注SimpleApplicationEventMulticaster,因?yàn)檫@是默認(rèn)的實(shí)現(xiàn),但請(qǐng)注意,Spring允許替換為自定義的ApplicationEventMulticaster。
以下是SimpleApplicationEventMulticaster中的相關(guān)代碼與分析,有興趣的小伙伴可以自行查看:
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
// 可選的任務(wù)執(zhí)行器,用于異步調(diào)用事件監(jiān)聽(tīng)器。
@Nullable
private Executor taskExecutor;
// 可選的錯(cuò)誤處理器,用于處理在廣播事件過(guò)程中出現(xiàn)的錯(cuò)誤。
@Nullable
private ErrorHandler errorHandler;
// 用于記錄日志的logger,它是延遲初始化的。
@Nullable
private volatile Log lazyLogger;
// 默認(rèn)構(gòu)造函數(shù)。
public SimpleApplicationEventMulticaster() {
}
// 帶有BeanFactory參數(shù)的構(gòu)造函數(shù),通常用于更復(fù)雜的應(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;
}
// 獲取當(dāng)前設(shè)置的任務(wù)執(zhí)行器。
@Nullable
protected Executor getTaskExecutor() {
return this.taskExecutor;
}
// 設(shè)置錯(cuò)誤處理器。
public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
// 獲取當(dāng)前設(shè)置的錯(cuò)誤處理器。
@Nullable
protected ErrorHandler getErrorHandler() {
return this.errorHandler;
}
// 這是廣播事件的主要方法。它首先解析事件的類(lèi)型,然后調(diào)用具有額外參數(shù)的重載方法。
public void multicastEvent(ApplicationEvent event) {
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
// 這個(gè)方法是真正執(zhí)行廣播操作的方法。
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
// 確定事件類(lèi)型。
ResolvableType type = (eventType != null) ? eventType : this.resolveDefaultEventType(event);
// 獲取任務(wù)執(zhí)行器。
Executor executor = this.getTaskExecutor();
// 獲取匹配此事件類(lèi)型的所有監(jiān)聽(tīng)器。
Iterator<ApplicationListener<?>> listeners = this.getApplicationListeners(event, type).iterator();
// 遍歷每個(gè)監(jiān)聽(tīng)器并調(diào)用它。
while(listeners.hasNext()) {
ApplicationListener<?> listener = listeners.next();
// 如果有設(shè)置任務(wù)執(zhí)行器,則異步調(diào)用監(jiān)聽(tīng)器。
if (executor != null) {
executor.execute(() -> this.invokeListener(listener, event));
} else {
// 如果沒(méi)有設(shè)置任務(wù)執(zhí)行器,則同步調(diào)用監(jiān)聽(tīng)器。
this.invokeListener(listener, event);
}
}
}
// 為給定的事件解析默認(rèn)的事件類(lèi)型。
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}
// 調(diào)用指定的監(jiān)聽(tīng)器來(lái)處理給定的事件,并根據(jù)需要處理錯(cuò)誤。
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 獲取當(dāng)前的錯(cuò)誤處理器。
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
// 嘗試調(diào)用監(jiān)聽(tīng)器。
this.doInvokeListener(listener, event);
} catch (Throwable ex) {
// 如果出現(xiàn)錯(cuò)誤,使用錯(cuò)誤處理器處理。
errorHandler.handleError(ex);
}
} else {
// 如果沒(méi)有設(shè)置錯(cuò)誤處理器,則直接調(diào)用監(jiān)聽(tīng)器。
this.doInvokeListener(listener, event);
}
}
// 直接調(diào)用監(jiān)聽(tīng)器,捕獲任何類(lèi)型不匹配的異常。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
// 捕獲類(lèi)型轉(zhuǎn)換異常,并根據(jù)需要進(jìn)行處理。
// 這可以確保如果監(jiān)聽(tīng)器不能處理特定類(lèi)型的事件,不會(huì)導(dǎo)致整個(gè)廣播操作失敗。
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ù)給定的類(lèi)型錯(cuò)誤消息和事件類(lèi)來(lái)檢查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需要作出特別說(shuō)明:
// 這個(gè)方法是真正執(zhí)行廣播操作的方法。
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
// 確定事件類(lèi)型。
ResolvableType type = (eventType != null) ? eventType : this.resolveDefaultEventType(event);
......
// 獲取匹配此事件類(lèi)型的所有監(jiān)聽(tīng)器。
Iterator<ApplicationListener<?>> listeners = this.getApplicationListeners(event, type).iterator();
......
}
-----------------------------------
?著作權(quán)歸作者所有:來(lái)自51CTO博客作者華為云開(kāi)發(fā)者聯(lián)盟的原創(chuàng)作品,請(qǐng)聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責(zé)任
掌握Spring事件監(jiān)聽(tīng)器的內(nèi)部邏輯與實(shí)現(xiàn)
https://blog.51cto.com/u_15214399/8112689
方法第一行得到一個(gè)ResolvableType類(lèi)型的對(duì)象,為什么 Spring 選擇使用 ResolvableType 而不是直接使用 Java 類(lèi)型?最主要的原因是 Java 的泛型擦除。 在 Java 中,泛型只存在于編譯時(shí),一旦代碼被編譯,泛型信息就會(huì)被擦除,運(yùn)行時(shí)就不能直接獲取到泛型的實(shí)際類(lèi)型。
為了解決這個(gè)問(wèn)題,Spring 引入了 ResolvableType,一個(gè)能夠解析泛型類(lèi)型信息的工具類(lèi)。
舉個(gè)例子:
假設(shè)有如下的類(lèi)定義:
public class Sample {
private List<String> names;
}我們可以這樣獲取 names 字段的泛型類(lèi)型:
ResolvableType t = ResolvableType.forField(Sample.class.getDeclaredField("names"));
Class<?> genericType = t.getGeneric(0).resolve(); // 得到 String.class在 Spring 事件中的使用
ResolvableType 在 Spring 事件中的應(yīng)用主要是確定事件的類(lèi)型和監(jiān)聽(tīng)器監(jiān)聽(tīng)的事件類(lèi)型。當(dāng)我們發(fā)布一個(gè)事件:
ApplicationEvent event = new MyCustomEvent(this, "data");
applicationContext.publishEvent(event);Spring 內(nèi)部會(huì)使用 ResolvableType.forInstance(event) 來(lái)獲取這個(gè)事件的類(lèi)型。然后,它會(huì)找到所有注冊(cè)的監(jiān)聽(tīng)器,查看它們監(jiān)聽(tīng)的事件類(lèi)型是否與此事件匹配(通過(guò)比較 ResolvableType)。匹配的監(jiān)聽(tīng)器會(huì)被調(diào)用。
對(duì)于一個(gè)監(jiān)聽(tīng)器:
public class MyListener implements ApplicationListener<MyCustomEvent> {
@Override
public void onApplicationEvent(MyCustomEvent event) {
// handle the event
}
}Spring 內(nèi)部會(huì)使用 ResolvableType 來(lái)解析這個(gè)監(jiān)聽(tīng)器監(jiān)聽(tīng)的事件類(lèi)型(在這個(gè)例子中是 MyCustomEvent)。
總之,ResolvableType 在 Spring 中的主要用途是提供了一種方式來(lái)解析和操作運(yùn)行時(shí)的泛型類(lèi)型信息,特別是在事件發(fā)布和監(jiān)聽(tīng)中。
4.4 Spring事件發(fā)布與處理流程圖
如果看不清,建議在新標(biāo)簽頁(yè)中打開(kāi)圖片后放大看

4.5 監(jiān)聽(tīng)器內(nèi)部邏輯
再來(lái)看看監(jiān)聽(tīng)器內(nèi)部邏輯,我們來(lái)分析在multicastEvent方法中調(diào)用的getApplicationListeners(event, type)來(lái)分析下
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
// 獲取事件來(lái)源對(duì)象
Object source = event.getSource();
// 判斷事件來(lái)源對(duì)象是否為null,是則返回null,否則返回事件來(lái)源對(duì)象的類(lèi)
Class<?> sourceType = source != null ? source.getClass() : null;
// 使用事件類(lèi)型和源類(lèi)型作為緩存鍵
AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
// 初始化一個(gè)新的監(jiān)聽(tīng)器檢索器為null
AbstractApplicationEventMulticaster.CachedListenerRetriever newRetriever = null;
// 嘗試從緩存中使用鍵取得一個(gè)已存在的檢索器
AbstractApplicationEventMulticaster.CachedListenerRetriever existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);
// 如果沒(méi)有從緩存中獲取到檢索器,并且滿(mǎn)足緩存安全性條件
if (existingRetriever == null && (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// 創(chuàng)建一個(gè)新的檢索器
newRetriever = new AbstractApplicationEventMulticaster.CachedListenerRetriever();
// 嘗試將新檢索器添加到緩存中
existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
// 如果緩存中已經(jīng)有了一個(gè)值(由于并發(fā)的原因),則將新檢索器設(shè)回null
if (existingRetriever != null) {
newRetriever = null;
}
}
// 如果有現(xiàn)有的檢索器
if (existingRetriever != null) {
// 嘗試從檢索器中獲取監(jiān)聽(tīng)器集合
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
// 如果結(jié)果不為null,則直接返回
if (result != null) {
return result;
}
}
// 如果上述步驟都沒(méi)有返回,調(diào)用retrieveApplicationListeners進(jìn)行實(shí)際的監(jiān)聽(tīng)器檢索
return this.retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable AbstractApplicationEventMulticaster.CachedListenerRetriever retriever) {
// 初始化一個(gè)空的監(jiān)聽(tīng)器列表
List<ApplicationListener<?>> allListeners = new ArrayList();
// 若retriever非null,則初始化集合來(lái)保存過(guò)濾出來(lái)的監(jiān)聽(tīng)器和Bean名
Set<ApplicationListener<?>> filteredListeners = retriever != null ? new LinkedHashSet() : null;
Set<String> filteredListenerBeans = retriever != null ? new LinkedHashSet() : null;
LinkedHashSet listeners;
LinkedHashSet listenerBeans;
// 同步從defaultRetriever中獲取已注冊(cè)的監(jiān)聽(tīng)器和其Bean名稱(chēng)
synchronized(this.defaultRetriever) {
listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
}
// 遍歷所有的監(jiān)聽(tīng)器
for (ApplicationListener<?> listener : listeners) {
// 檢查該監(jiān)聽(tīng)器是否支持此事件類(lèi)型和源類(lèi)型
if (this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
// 如果支持并且retriever非null,添加到過(guò)濾監(jiān)聽(tīng)器集合
filteredListeners.add(listener);
}
// 將支持的監(jiān)聽(tīng)器添加到allListeners列表
allListeners.add(listener);
}
}
// 如果存在監(jiān)聽(tīng)器Bean名稱(chēng)
if (!listenerBeans.isEmpty()) {
ConfigurableBeanFactory beanFactory = this.getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
// 檢查Bean工廠(chǎng)中的Bean是否支持該事件
if (this.supportsEvent(beanFactory, listenerBeanName, eventType)) {
ApplicationListener<?> listener = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
// 再次檢查確保Bean實(shí)例支持事件,并且它還沒(méi)有被加入allListeners列表
if (!allListeners.contains(listener) && this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
// 若該Bean是單例并且retriever非null,添加到過(guò)濾監(jiān)聽(tīng)器集合
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ù)下一個(gè)
}
}
}
// 對(duì)allListeners列表進(jìn)行排序,確保監(jiān)聽(tīng)器的執(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)歸作者所有:來(lái)自51CTO博客作者華為云開(kāi)發(fā)者聯(lián)盟的原創(chuàng)作品,請(qǐng)聯(lián)系作者獲取轉(zhuǎn)載授權(quán),否則將追究法律責(zé)任
掌握Spring事件監(jiān)聽(tīng)器的內(nèi)部邏輯與實(shí)現(xiàn)
https://blog.51cto.com/u_15214399/8112689監(jiān)聽(tīng)器內(nèi)部做了什么?
在getApplicationListeners方法中,采用了一種優(yōu)化檢索的緩存機(jī)制來(lái)提高性能并確保線(xiàn)程安全性。
具體分析如下:
首次檢查:
AbstractApplicationEventMulticaster.CachedListenerRetriever existingRetriever =
(AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.get(cacheKey);這里,我們首先從retrieverCache中檢索existingRetriever。
判斷是否需要進(jìn)入同步代碼塊:
if (existingRetriever == null && (this.beanClassLoader == null || ...)) {
...
}
如果existingRetriever為空,那么我們可能需要?jiǎng)?chuàng)建一個(gè)新的CachedListenerRetriever并放入緩存中。但是,為了確保線(xiàn)程安全性,我們必須在這之前進(jìn)行進(jìn)一步的檢查。
雙重檢查:
在創(chuàng)建新的CachedListenerRetriever之前,我們使用了putIfAbsent方法。這個(gè)方法會(huì)嘗試添加一個(gè)新值,但如果該值已存在,它只會(huì)返回現(xiàn)有的值。該機(jī)制采用了一種緩存優(yōu)化策略:通過(guò)ConcurrentMap的putIfAbsent方法,即使多個(gè)線(xiàn)程同時(shí)到達(dá)這個(gè)代碼段,也確保只有一個(gè)線(xiàn)程能夠成功地放入新的值,從而保證線(xiàn)程安全性。
newRetriever = new AbstractApplicationEventMulticaster.CachedListenerRetriever();
existingRetriever = (AbstractApplicationEventMulticaster.CachedListenerRetriever)this.retrieverCache.putIfAbsent(cacheKey, newRetriever);這里的邏輯使用了ConcurrentMap的putIfAbsent方法來(lái)確保線(xiàn)程安全性,而沒(méi)有使用傳統(tǒng)的synchronized塊。
所以,我們可以說(shuō)getApplicationListeners中的這部分邏輯采用了一種優(yōu)化檢索的緩存機(jī)制。它利用了并發(fā)容器的原子性操作putIfAbsent來(lái)保證線(xiàn)程安全,而不是依賴(lài)于傳統(tǒng)的雙重檢查鎖定模式。
總體概括一下,對(duì)于getApplicationListeners和retrieveApplicationListeners兩個(gè)方法的功能可以總結(jié)為以下三個(gè)步驟:
從默認(rèn)檢索器篩選監(jiān)聽(tīng)器:
這部分代碼直接從defaultRetriever中獲取監(jiān)聽(tīng)器,并檢查它們是否支持當(dāng)前事件。在retrieveApplicationListeners方法中,代碼首先從defaultRetriever中獲取已經(jīng)編程式注入的監(jiān)聽(tīng)器,并檢查每個(gè)監(jiān)聽(tīng)器是否支持當(dāng)前的事件類(lèi)型。
listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
for (ApplicationListener<?> listener : listeners) {
if (this.supportsEvent(listener, eventType, sourceType)) {
... // 添加到篩選出來(lái)的監(jiān)聽(tīng)器列表
}
}從IOC容器中篩選監(jiān)聽(tīng)器:
在retrieveApplicationListeners方法中,除了從defaultRetriever中獲取已經(jīng)編程式注入的監(jiān)聽(tīng)器,代碼還會(huì)嘗試從IOC容器(通過(guò)bean名稱(chēng))獲取監(jiān)聽(tīng)器,并檢查它們是否支持當(dāng)前的事件。
if (!listenerBeans.isEmpty()) {
ConfigurableBeanFactory beanFactory = this.getBeanFactory();
for (String listenerBeanName : listenerBeans) {
... // 檢查并添加到篩選出來(lái)的監(jiān)聽(tīng)器列表
}
}
監(jiān)聽(tīng)器排序:
最后,為確保監(jiān)聽(tīng)器按照預(yù)定的順序響應(yīng)事件,篩選出的所有監(jiān)聽(tīng)器會(huì)經(jīng)過(guò)排序。排序基于Spring的@Order注解或Ordered接口,如AnnotationAwareOrderComparator.sort(allListeners)所示
AnnotationAwareOrderComparator.sort(allListeners);4.6 Spring事件監(jiān)聽(tīng)器檢索流程圖

5. Spring事件傳播、異步處理等機(jī)制的詳細(xì)圖示

說(shuō)明:
容器和事件廣播:
ApplicationContext 是Spring的應(yīng)用上下文容器。在圖中,我們有一個(gè)主容器和一個(gè)子容器。
當(dāng)我們想發(fā)布一個(gè)事件時(shí),我們調(diào)用 publishEvent 方法。
ApplicationEventMulticaster 負(fù)責(zé)實(shí)際地將事件廣播到各個(gè)監(jiān)聽(tīng)器。
主容器和子容器關(guān)系:
在Spring中,可以有多個(gè)容器,其中一個(gè)是主容器,其他的則是子容器。
通常,子容器可以訪(fǎng)問(wèn)主容器中的bean,但反之則不行。但在事件傳播的上下文中,子容器發(fā)布的事件默認(rèn)不會(huì)在主容器中傳播。這一點(diǎn)由 Note1 注釋標(biāo)明。
異步處理:
當(dāng)事件被發(fā)布時(shí),它可以被異步地傳播到監(jiān)聽(tīng)器,這取決于是否配置了異步執(zhí)行器。
是否使用異步執(zhí)行器? 這個(gè)決策點(diǎn)說(shuō)明了基于配置,事件可以同步或異步地傳播到監(jiān)聽(tīng)器。
事件生命周期:
在Spring容器的生命周期中,有些事件在容器初始化前觸發(fā),這些被稱(chēng)為 early events。這些事件會(huì)被緩存起來(lái),直到容器初始化完成。
一旦容器初始化完成,這些早期的事件會(huì)被處理,并開(kāi)始處理常規(guī)事件。
在容器銷(xiāo)毀時(shí),也可能觸發(fā)事件。
注意事項(xiàng) (Note1):
這個(gè)部分強(qiáng)調(diào)了一個(gè)特定的行為,即在某些配置下,子容器發(fā)布的事件可能也會(huì)在主容器中傳播,但這并不是默認(rèn)行為。





















