Spring源碼之事件監(jiān)聽機(jī)制(實(shí)現(xiàn)EventListener接口方式)
一、Spring實(shí)現(xiàn)自定義事件的發(fā)布訂閱
1、事件定義
/**
* 定義事件類型
*
* @author lihongmin
* @date 2019/11/3 20:30
*/
public class OrderEvent extends ApplicationEvent {
public OrderEvent(Object source) {
super(source);
}
}2、事件監(jiān)聽(泛型)
/**
* 訂單事件監(jiān)聽
* @author lihongmin
* @date 2019/11/3 20:33
*/
@Component
public class OrderEventListener implements ApplicationListener<OrderEvent> {
@Override
public void onApplicationEvent(OrderEvent orderEvent) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我受到了一個事件:" + orderEvent.getSource());
}
}3、模擬事件發(fā)送
/**
* 事件觸發(fā)模擬
*
* 我受到了一個事件:我發(fā)布了事件!??!
* 我執(zhí)行完畢了?。。?
*
* @author lihongmin
* @date 2019/11/3 20:35
*/
@Controller
public class OrderEventController implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@GetMapping("publishOrderEvent")
public String publishOrderEvent() {
applicationContext.publishEvent(new OrderEvent("我發(fā)布了事件?。?!"));
System.out.println("我執(zhí)行完畢了?。?!");
return "發(fā)送事件了!";
}
}4、啟動項目,調(diào)用 127.0.0.1:8080/publishOrderEvent
我受到了一個事件:我發(fā)布了事件?。?!
我執(zhí)行完畢了?。。?/p>
總結(jié):事件發(fā)送非常的簡單,一個事件類型,一個監(jiān)聽,一個觸發(fā)機(jī)制。并且該事件為同步機(jī)制(后續(xù)在Spring Boot中可以方便切換為異步)。
二、Spring事件驅(qū)動原理分析(Spring版本為5.1.7)
1、ApplicationContext委派ApplicationEventPublisher發(fā)送事件
我們調(diào)用的是 ApplicationContext的
publishEvent(new OrderEvent("我發(fā)布了事件?。。?)); 查看ApplicationContext 結(jié)構(gòu),發(fā)現(xiàn)調(diào)用的是父類 ApplicationEventPublisher的接口
如下:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory,
HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
@Nullable
String getId();
String getApplicationName();
String getDisplayName();
long getStartupDate();
@Nullable
ApplicationContext getParent();
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
void publishEvent(Object var1);
}那么就是其子類 AbstractApplicationContext 實(shí)現(xiàn)的發(fā)送操作
public void publishEvent(Object event) {
this.publishEvent(event, (ResolvableType)null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
Object applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}發(fā)現(xiàn)執(zhí)行到
getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
那么其實(shí)這里算是一個委派模式了(個人認(rèn)為),spring容器將發(fā)送事件委派給 AbstractApplicationContext的ApplicationEventMulticaster applicationEventMulticaster對象。
2、ApplicationEventMutulcaster類型的確認(rèn)和初始化
不難發(fā)現(xiàn)(或者對Spring ApplicationContext比較熟悉的話)是項目啟動時,不同類型的ApplicationContext(如:ClassPathXmlApplicationContext)
在調(diào)用父類 AbstractApplicationContext的refresh方法(之前分析過是一個模板方法)時, initApplicationEventMulticaster()
如下:
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No 'applicationEventMulticaster' bean, using [" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}邏輯比較簡單,在BeanFactory中獲取名稱為 applicationEventMulticaster的Bean,當(dāng)然如果我們沒有自定義并且注冊為該名稱的Bean,肯定是獲取不到的。
那么會new一個 SimpleApplicationEventMulticaster類型的bean注冊到容器中。
也就是說上面的getApplicationEventMulticaster()獲取到的就是SimpleApplicationEventMulticaster。
但是還需要注意使用的是有參數(shù)構(gòu)造進(jìn)行初始化,如下:
public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {
this.setBeanFactory(beanFactory);
}在父類中實(shí)現(xiàn):
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
if (beanFactory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory cbf = (ConfigurableBeanFactory)beanFactory;
if (this.beanClassLoader == null) {
this.beanClassLoader = cbf.getBeanClassLoader();
}
this.retrievalMutex = cbf.getSingletonMutex();
}
}獲取bean工廠中所以的所以單例對象放入屬性retrievalMutex 中,將類加載器也進(jìn)行賦值,后續(xù)會用到。
3、SimpleApplicationEventMulticaster的發(fā)送事件方法
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}分析一下這個方法:
- 1)、獲取或確認(rèn) ResolvableType 類型
- 2)、根據(jù)事件對象和ResolvableType 類型,獲取訂閱者列表
- 3)、發(fā)現(xiàn)如果 SimpleApplicationEventMulticaster對象的線程池屬性 Executor taskExecutor不為null則異步執(zhí)行監(jiān)聽方法。但是我們看到的是自己new了一個對象,所以如果想 事件監(jiān)聽使用線程池異步執(zhí)行的話(自己想到應(yīng)該可以這樣玩,自己比較喜歡自定義線程參數(shù),心里有數(shù),當(dāng)前一般還會設(shè)置線程池前綴名稱):
@Component
public class DesignpatternApplication implements BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Bean("APPLICATION_EVENT_MULTICASTER_BEAN_NAME")
public SimpleApplicationEventMulticaster init() {
ThreadPoolExecutor MulticasterExecutor = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(MulticasterExecutor);
multicaster.setBeanFactory(beanFactory);
return multicaster;
}
}- 4)、最后肯定是invokeListener(listener, event);
4、ResolvableType類型確認(rèn)
首先我們傳入的eventType是null,所以先根據(jù)我們傳入的對象調(diào)用resolveDefaultEventType方法
如下:
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {
return ResolvableType.forInstance(event);
}再調(diào)用,肯定OrderEvent肯定沒有實(shí)現(xiàn)ResolvableTypeProvider接口:
public static ResolvableType forInstance(Object instance) {
Assert.notNull(instance, "Instance must not be null");
if (instance instanceof ResolvableTypeProvider) {
ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();
if (type != null) {
return type;
}
}
return ResolvableType.forClass(instance.getClass());
}再調(diào)用:
public static ResolvableType forClass(@Nullable Class<?> clazz) {
return new ResolvableType(clazz);
}所以我們或者到了一個新創(chuàng)建的 ResolvableType 對象,對象的clazz字段為我們的 OrderEvent。
為什么追這么深,是因為下面就是根據(jù)類型來獲取監(jiān)聽器的。
5、獲取所有的監(jiān)聽列表,并且看看是怎么做到監(jiān)聽泛型類型
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = source != null ? source.getClass() : null;
AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
AbstractApplicationEventMulticaster.ListenerRetriever retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
} else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {
Object var7 = this.retrievalMutex;
synchronized(this.retrievalMutex) {
retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
if (retriever != null) {
return retriever.getApplicationListeners();
} else {
retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever);
return listeners;
}
}
} else {
return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null);
}
}在自己的 ConcurrentHashMap類型的retrieverCache緩存中獲取,key是根據(jù) OrderEvent類型和我發(fā)送的數(shù)據(jù)源(當(dāng)前為String類型)如下:
- Map的key:
private static final class ListenerCacheKey implements
Comparable<AbstractApplicationEventMulticaster.ListenerCacheKey> {
private final ResolvableType eventType;
@Nullable
private final Class<?> sourceType;
// .....
}- Map的value類型:
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners =
new LinkedHashSet();
public final Set<String> applicationListenerBeans = new LinkedHashSet();
private final boolean preFiltered;
}很清楚的結(jié)構(gòu),兩個LinkedHashSet, 就是為了保證兩個Set個數(shù)相同,并且順序一一對應(yīng)。用于存放當(dāng)前的監(jiān)聽對象和監(jiān)聽的類型。
當(dāng)前的緩存是在AbstractApplicationContext的refresh的registerBeanPostProcessors(注冊所有的BeanPostProcess),的最后一步,注冊了ApplicationListenerDetector類型。
并且在refresh的最后會將所有懶加載的Bean都初始化,則會將所有的實(shí)現(xiàn)了該接口的Bean放入容器中。
則重點(diǎn)是 retrieveApplicationListeners方法,比較長:
private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable AbstractApplicationEventMulticaster.ListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList();
Object var7 = this.retrievalMutex;
LinkedHashSet listeners;
LinkedHashSet listenerBeans;
synchronized(this.retrievalMutex) {
listeners = new LinkedHashSet(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet(this.defaultRetriever.applicationListenerBeans);
}
Iterator var14 = listeners.iterator();
while(var14.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var14.next();
if (this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
allListeners.add(listener);
}
}
if (!listenerBeans.isEmpty()) {
BeanFactory beanFactory = this.getBeanFactory();
Iterator var16 = listenerBeans.iterator();
while(var16.hasNext()) {
String listenerBeanName = (String)var16.next();
try {
Class<?> listenerType = beanFactory.getType(listenerBeanName);
if (listenerType == null || this.supportsEvent(listenerType, eventType)) {
ApplicationListener<?> listener = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener) && this.supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
if (beanFactory.isSingleton(listenerBeanName)) {
retriever.applicationListeners.add(listener);
} else {
retriever.applicationListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
} catch (NoSuchBeanDefinitionException var13) {
;
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
return allListeners;
}分析該方法,上面鎖住的是 retrievalMutex對象,現(xiàn)在又是同步鎖該對象。
為了保證LinkedHashSet中的值不會亂(monitor enter兩次exit兩次),去緩存中的每個查看每個監(jiān)聽器是否是對象的類型,檢查了監(jiān)聽器的泛型對象和事件源類型。
6、根據(jù)監(jiān)聽列表,循環(huán)調(diào)用(同步或異步)
我們實(shí)現(xiàn)的 onApplicationEvent(OrderEvent orderEvent)方法
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
}
}所以 ErrorHandler想在這里處理,則需要在該對象中創(chuàng)建該異常處理器(可以有很多中方式處理,利用bean的生命周期,這是一個很好的擴(kuò)展點(diǎn),后續(xù)可以去實(shí)現(xiàn)),繼續(xù) doInvokeListener方法
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, var6);
}
}
}最后看見 listener.onApplicationEvent(event);
it is over!??!
總結(jié)
1、ApplicationContext發(fā)送事件是委托給了一個 Spring容器在refresh時初始化的SimpleApplicationEventMulticaster bean(由于沒有初始化內(nèi)部線程池對象,所以事件是同步發(fā)送的)。
2、發(fā)送前先獲取事件的ResolvableType類型(當(dāng)前為OrderEvent clazz)和事件源類型(當(dāng)前為String)
3、獲取監(jiān)聽者列表。 先去自己Bean內(nèi)部先查詢緩存,否則從BeanFactory中獲取所有單利bean進(jìn)行匹配(再放入緩存ConturrentHashMap)。
4、監(jiān)聽者列表循環(huán)(同步或異步)地調(diào)用我們自己寫的監(jiān)聽方法OnApplicationEvent。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
利用EasyPOI實(shí)現(xiàn)多sheet和列數(shù)的動態(tài)生成
EasyPoi功能如同名字,主打的功能就是容易,讓一個沒見接觸過poi的人員就可以方便的寫出Excel導(dǎo)出,Excel導(dǎo)入等功能,本文主要來講講如何利用EasyPOI實(shí)現(xiàn)多sheet和列數(shù)的動態(tài)生成,需要的可以了解下2025-03-03
SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法
Spring Boot Actuator 是開發(fā)和管理生產(chǎn)級 Spring Boot 應(yīng)用程序的重要工具,它可以幫助你確保應(yīng)用程序的穩(wěn)定性和性能,本文給大家介紹了SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法,需要的朋友可以參考下2024-05-05
SpringBoot中的配置類(@Configuration)
這篇文章主要介紹了SpringBoot中的配置類(@Configuration),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
java基于C/S模式實(shí)現(xiàn)聊天程序(客戶端)
這篇文章主要為大家詳細(xì)介紹了java基于C/S模式實(shí)現(xiàn)聊天程序,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01

