SpringBoot實(shí)現(xiàn)異步事件驅(qū)動(dòng)的方法
在項(xiàng)目實(shí)際開發(fā)過程中,我們有很多這樣的業(yè)務(wù)場(chǎng)景:一個(gè)事務(wù)中處理完一個(gè)業(yè)務(wù)邏輯后需要跟著處理另外一個(gè)業(yè)務(wù)邏輯,偽碼大致如下:
@Service
public class ProductServiceImpl {
...
public void saveProduct(Product product) {
productMapper.saveOrder(product);
notifyService.notify(product);
}
...
}
很簡(jiǎn)單并且很常見的一段業(yè)務(wù)邏輯:首先將產(chǎn)品先保存數(shù)據(jù)庫(kù),然后發(fā)送通知。
某一天你們可能需要把新增的產(chǎn)品存到Es中,這時(shí)候也需要代碼可能變成這樣:
@Service
public class ProductServiceImpl {
...
public void saveProduct(Product product) {
productMapper.saveProduct(product);
esService.saveProduct(product)
notifyService.notify(product);
}
...
}
隨著業(yè)務(wù)需求的變化,代碼也需要跟著一遍遍的修改。而且還會(huì)存在另外一個(gè)問題,如果通知系統(tǒng)掛了,那就不能再新增產(chǎn)品了。
對(duì)于上面這種情況非常適合引入消息中間件(消息隊(duì)列)來對(duì)業(yè)務(wù)進(jìn)行解耦,但并非所有的業(yè)務(wù)系統(tǒng)都會(huì)引入消息中間件(引入會(huì)第三方架構(gòu)組件會(huì)帶來很大的運(yùn)維成本)。
Spring提供了事件驅(qū)動(dòng)機(jī)制可以幫助我們實(shí)現(xiàn)這一需求。
Spring事件驅(qū)動(dòng)
spring事件驅(qū)動(dòng)由3個(gè)部分組成
- ApplicationEvent:表示事件本身,自定義事件需要繼承該類,用來定義事件
- ApplicationEventPublisher:事件發(fā)送器,主要用來發(fā)布事件
- ApplicationListener:事件監(jiān)聽器接口,監(jiān)聽類實(shí)現(xiàn)ApplicationListener 里onApplicationEvent方法即可,也可以在方法上增加@EventListener以實(shí)現(xiàn)事件監(jiān)聽。
實(shí)現(xiàn)Spring事件驅(qū)動(dòng)一般只需要三步:
- 自定義需要發(fā)布的事件類,需要繼承ApplicationEvent類
- 使用ApplicationEventPublisher來發(fā)布自定義事件
- 使用@EventListener來監(jiān)聽事件
這里需要特別注意一點(diǎn),默認(rèn)情況下事件是同步的。即事件被publish后會(huì)等待Listener的處理。如果發(fā)布事件處的業(yè)務(wù)存在事務(wù),監(jiān)聽器處理也會(huì)在相同的事務(wù)中。如果需要異步處理事件,可以onApplicationEvent方法上加@Aync支持異步或在有@EventListener的注解方法上加上@Aync。
源碼實(shí)戰(zhàn)
創(chuàng)建事件
public class ProductEvent extends ApplicationEvent {
public ProductEvent(Product product) {
super(product);
}
}
發(fā)布事件
@Service
public class ProductServiceImpl implements IproductService {
...
@Autowired
private ApplicationEventPublisher publisher;
@Override
@Transactional(rollbackFor = Exception.class)
public void saveProduct(Product product) {
productMapper.saveProduct(product);
//事件發(fā)布
publisher.publishEvent(product);
}
...
}
事件監(jiān)聽
@Slf4j
@AllArgsConstructor
public class ProductListener {
private final NotifyService notifyServcie;
@Async
@Order
@EventListener(ProductEvent.class)
public void notify(ProductEvent event) {
Product product = (Product) event.getSource();
notifyServcie.notify(product, "product");
}
}
在SpringBoot啟動(dòng)類上增加@EnableAsync 注解
@Slf4j
@EnableSwagger2
@SpringBootApplication
@EnableAsync
public class ApplicationBootstrap {
...
}
使用了Async后會(huì)使用默認(rèn)的線程池SimpleAsyncTaskExecutor,一般我們會(huì)在項(xiàng)目中自定義一個(gè)線程池。
@Configuration
public class ExecutorConfig {
/** 核心線程數(shù) */
private int corePoolSize = 10;
/** 最大線程數(shù) */
private int maxPoolSize = 50;
/** 隊(duì)列大小 */
private int queueCapacity = 10;
/** 線程最大空閑時(shí)間 */
private int keepAliveSeconds = 150;
@Bean("customExecutor")
public Executor myExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("customExecutor-");
executor.setKeepAliveSeconds(keepAliveSeconds);
// rejection-policy:當(dāng)pool已經(jīng)達(dá)到max size的時(shí)候,如何處理新任務(wù)
// CALLER_RUNS:不在新線程中執(zhí)行任務(wù),而是由調(diào)用者所在的線程來執(zhí)行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)異步事件驅(qū)動(dòng)的方法的文章就介紹到這了,更多相關(guān)SpringBoot 異步事件驅(qū)動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢(shì)
這篇文章主要為大家詳細(xì)介紹一下SpringBoot實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的最佳姿勢(shì),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
詳解spring cloud整合Swagger2構(gòu)建RESTful服務(wù)的APIs
這篇文章主要介紹了詳解spring cloud整合Swagger2構(gòu)建RESTful服務(wù)的APIs,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01
Java常見延遲隊(duì)列的實(shí)現(xiàn)方案總結(jié)
Java延遲隊(duì)列(DelayQueue)是Java并發(fā)包中的一個(gè)類,它實(shí)現(xiàn)了BlockingQueue接口,且其中的元素必須實(shí)現(xiàn)Delayed接口,延遲隊(duì)列中的元素按照延遲時(shí)間的長(zhǎng)短進(jìn)行排序,本文給大家介紹了Java常見延遲隊(duì)列的實(shí)現(xiàn)方案總結(jié),需要的朋友可以參考下2024-03-03

