Spring注解驅(qū)動(dòng)之ApplicationListener異步處理事件說(shuō)明
概述
之前我們講過(guò)簡(jiǎn)單使用ApplicationListener發(fā)布事件,處理事件,但是發(fā)現(xiàn)是同一個(gè)線程發(fā)送事件并自己處理事件的。
下面我們就來(lái)說(shuō)下如何使用自定義的線程池來(lái)異步處理接收的事件。
示例
實(shí)現(xiàn)一個(gè)ApplicationListener用于處理事件
package com.atguigu.ext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.stereotype.Component;
@Component
public class MyApplicationListener implements ApplicationListener<PayloadApplicationEvent> {
public void onApplicationEvent(PayloadApplicationEvent applicationEvent) {
System.out.println("exe thread start:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
System.out.println("收到事件:" + applicationEvent);
System.out.println(applicationEvent.getPayload());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("exe thread end:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
}
}
自定義事件多波器
package com.atguigu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
@ComponentScan("com.atguigu.ext")
public class ExtConfig {
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
BlockingQueue<Runnable> blockingQueue = new LinkedBlockingDeque<>(1000);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 10, TimeUnit.SECONDS, blockingQueue);
simpleApplicationEventMulticaster.setTaskExecutor(threadPoolExecutor);
return simpleApplicationEventMulticaster;
}
}
之前看源碼可以發(fā)現(xiàn),在容器創(chuàng)建的refresh方法中的initApplicationEventMulticaster()方法執(zhí)行時(shí),先從容器中獲取name為applicationEventMulticaster的組件,如果獲取不到就好創(chuàng)建一個(gè)默認(rèn)的applicationEventMulticaster組件,該組件默認(rèn)是不會(huì)設(shè)置taskExecutor任務(wù)執(zhí)行器的,所以這里我們自定義一個(gè)設(shè)置了TaskExecutor的多波器,當(dāng)執(zhí)行initApplicationEventMulticaster方法從beanFactory中獲取applicationEventMulticaster組件時(shí),走getBean邏輯。
BeanFactory.getBean()邏輯是先從容器查看是否有該組件,如果沒(méi)有獲取該組件的定義,如果有定義就會(huì)創(chuàng)建一個(gè)組件返回并把組件保存到容器中。
測(cè)試用例
package com.atguigu;
import com.atguigu.config.ExtConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @Description :
* @Version : V1.0.0
* @Date : 2022/9/1 15:07
*/
public class AnnotationTest {
public static void main(String[] args) {
final AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(ExtConfig.class);
System.out.println("main thread start:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
applicationContext.publishEvent("發(fā)送事件");
System.out.println("main thread end:" + Thread.currentThread().getName() + ", time:" + System.currentTimeMillis());
applicationContext.close();
}
}
測(cè)試結(jié)果
main thread start:main, time:1663499481185
main thread end:main, time:1663499481188
exe thread start:pool-1-thread-1, time:1663499481188
收到事件:org.springframework.context.PayloadApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@5ebec15: startup date [Sun Sep 18 19:11:20 CST 2022]; root of context hierarchy]
發(fā)送事件
九月 18, 2022 7:11:21 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5ebec15: startup date [Sun Sep 18 19:11:20 CST 2022]; root of context hierarchy
exe thread end:pool-1-thread-1, time:1663499484198
通過(guò)測(cè)試結(jié)果可以看出,main線程很快就返回了,而實(shí)際處理事件的線程是pool-1-thread-1,等待了3s多才返回。
ApplicationListener異步執(zhí)行源碼分析
參考:Spring注解驅(qū)動(dòng)之ApplicationListener用法
與上面同步執(zhí)行不同的地方就是使用了自定義的多波器里面的線程池執(zhí)行了事件處理。

多波器的獲取。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Security中的Servlet過(guò)濾器體系代碼分析
這篇文章主要介紹了Spring Security中的Servlet過(guò)濾器體系,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
Java Web開(kāi)發(fā)項(xiàng)目中中文亂碼解決方法匯總
這篇文章主要為大家詳細(xì)匯總了Java Web開(kāi)發(fā)項(xiàng)目中中文亂碼的解決方法,分析了5種Java Web中文亂碼情況,感興趣的小伙伴們可以參考一下2016-05-05
詳解Java抽象類(lèi)與普通類(lèi)的區(qū)別
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Java抽象類(lèi)與普通類(lèi)的區(qū)別展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
SpringBoot項(xiàng)目URL訪問(wèn)異常的問(wèn)題處理
這篇文章主要介紹了SpringBoot項(xiàng)目URL訪問(wèn)異常的問(wèn)題處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
Java 實(shí)戰(zhàn)項(xiàng)目之在線點(diǎn)餐系統(tǒng)的實(shí)現(xiàn)流程
讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)在線點(diǎn)餐系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-11-11
一文搞懂MyBatis多數(shù)據(jù)源Starter實(shí)現(xiàn)
本文將實(shí)現(xiàn)一個(gè)MyBatis的Springboot的Starter包,引用這個(gè)Starter包后,僅需要提供少量配置信息,就能夠完成MyBatis多數(shù)據(jù)源的初始化和使用,需要的小伙伴可以參考一下2023-04-04
詳細(xì)解讀Druid數(shù)據(jù)庫(kù)連接池的使用
這篇文章主要介紹了Druid數(shù)據(jù)庫(kù)連接池的使用,數(shù)據(jù)庫(kù)連接池負(fù)責(zé)分配、管理和釋放數(shù)據(jù)庫(kù)連接,它允許應(yīng)用程序重復(fù)使用一個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)連接,而不是重新建立一個(gè),需要的朋友可以參考下2023-03-03

