Spring事件如何異步執(zhí)行?
環(huán)境:SpringBoot2.7.16
1. 簡介
ApplicationContext 中的事件處理是通過 ApplicationEvent 類和 ApplicationListener 接口提供的。如果在上下文中部署了實(shí)現(xiàn) ApplicationListener 接口的 Bean,那么每次 ApplicationEvent 發(fā)布到 ApplicationContext 時(shí),都會(huì)通知該 Bean。從本質(zhì)上講,這就是標(biāo)準(zhǔn)的觀察者設(shè)計(jì)模式。
從Spring 4.2開始,事件基礎(chǔ)設(shè)施得到了顯著改進(jìn),并提供了基于注釋的模型以及發(fā)布任意事件的能力(也就是說,不一定是從ApplicationEvent擴(kuò)展的對象)。當(dāng)這樣的對象被發(fā)布時(shí),我們將它包裝在一個(gè)事件中。
以下是一個(gè)簡單的事件應(yīng)用
1.1 定義事件對象
public class PackEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
public PackEvent(Object source) {
super(source);
}
}
1.2 定義事件監(jiān)聽
@Component
public class PackEventListener implements ApplicationListener<PackEvent> {
@Override
public void onApplicationEvent(PackEvent event) {
System.out.println("觸發(fā)事件...") ;
}
}
1.3 發(fā)布事件
@Resource
private ApplicationEventMulticaster eventMulticaster ;
public void run(ApplicationArguments args) throws Exception {
eventMulticaster.multicastEvent(new PackEvent("自定義Pack")) ;
}
以上Spring事件系統(tǒng)的完整應(yīng)用實(shí)例。在默認(rèn)情況下該種事件處理方式是同步的,也就是事件的發(fā)布者與事件的處理都是同一個(gè)線程中,那這就要求我們的事件處理程序不應(yīng)該處理復(fù)雜耗時(shí)的任務(wù),否則會(huì)影響我們的主業(yè)務(wù)系統(tǒng)。那如何異步處理事件呢?
2. 事件異步處理
2.1 通過@Async注解
該種方式是最簡單的方式了,開啟異步功能,在基于注解的事件監(jiān)聽方法上使用@Async注解。
開啟異步任務(wù)功能更
@EnableAsync
public class AppApplication {}
基于注解事件監(jiān)聽
@Async
@EventListener({PackEvent.class})
public void packEventListener(PackEvent event) {
System.out.printf("%s - 事件發(fā)生了...%s%n", Thread.currentThread().getName(), event.getSource()) ;
}
執(zhí)行結(jié)果
task-1 - 事件發(fā)生了...自定義Pack
線程名已經(jīng)變?yōu)榱藅ask-1。task-前綴是異步線程的默認(rèn)名。關(guān)于異步任務(wù)執(zhí)行應(yīng)用的線程池配置,查看下面這篇文章。
Spring任務(wù)調(diào)度&異步任務(wù)&Web異步請求三者如何配置線程池?
上面是基于注解的方式應(yīng)用異步執(zhí)行事件處理。對于在簡介中通過實(shí)現(xiàn)ApplicationListener接口的方式又該如何處理呢?
對于這種方式,我們可以通過兩種方式進(jìn)行處理:
2.2 自定義線程池
- 在事件監(jiān)聽處理程序中開啟異步線程
@Component
public class PackEventListener implements ApplicationListener<PackEvent> {
@Override
public void onApplicationEvent(PackEvent event) {
new Thread(() -> {
System.out.printf("%s觸發(fā)事件...%n", Thread.currentThread().getName()) ;
}).start() ;
}
}
- 自定義事件廣播器
@Bean
TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor() ;
taskExecutor.setThreadNamePrefix("pack-event-") ;
taskExecutor.setCorePoolSize(5) ;
taskExecutor.setQueueCapacity(100) ;
taskExecutor.setMaxPoolSize(5) ;
taskExecutor.initialize() ;
return taskExecutor ;
}
// 注意beanName必須為applicationEventMulticaster;下面的源碼中你將看到
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
SimpleApplicationEventMulticaster eventMulticaster(BeanFactory beanFactory) {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(beanFactory) ;
eventMulticaster.setTaskExecutor(taskExecutor()) ;
return eventMulticaster ;
}
通過這種方式也可以實(shí)現(xiàn)事件處理程序異步執(zhí)行。而這種方式的實(shí)現(xiàn)原理如下:
容器啟動(dòng)中的核心方法refresh中
public abstract class AbstractApplicationContext {
public void refresh() {
// 初始化事件廣播器
initApplicationEventMulticaster();
}
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判斷容器中是否存在beanName=applicationEventMulticaster
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
} else {
// 如果不存在則創(chuàng)建一個(gè)同步的執(zhí)行器。
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
}
}
}
以上是本篇文章的全部內(nèi)容,希望對你有幫助。
完畢?。?!