SpringBoot實現異步事件驅動的方法
在項目實際開發過程中,我們有很多這樣的業務場景:一個事務中處理完一個業務邏輯後需要跟著處理另外一個業務邏輯,偽碼大致如下:
@Service public class ProductServiceImpl { ... public void saveProduct(Product product) { productMapper.saveOrder(product); notifyService.notify(product); } ... }
很簡單並且很常見的一段業務邏輯:首先將產品先保存數據庫,然後發送通知。
某一天你們可能需要把新增的產品存到Es中,這時候也需要代碼可能變成這樣:
@Service public class ProductServiceImpl { ... public void saveProduct(Product product) { productMapper.saveProduct(product); esService.saveProduct(product) notifyService.notify(product); } ... }
隨著業務需求的變化,代碼也需要跟著一遍遍的修改。而且還會存在另外一個問題,如果通知系統掛瞭,那就不能再新增產品瞭。
對於上面這種情況非常適合引入消息中間件(消息隊列)來對業務進行解耦,但並非所有的業務系統都會引入消息中間件(引入會第三方架構組件會帶來很大的運維成本)。
Spring提供瞭事件驅動機制可以幫助我們實現這一需求。
Spring事件驅動
spring事件驅動由3個部分組成
- ApplicationEvent:表示事件本身,自定義事件需要繼承該類,用來定義事件
- ApplicationEventPublisher:事件發送器,主要用來發佈事件
- ApplicationListener:事件監聽器接口,監聽類實現ApplicationListener 裡onApplicationEvent方法即可,也可以在方法上增加@EventListener以實現事件監聽。
實現Spring事件驅動一般隻需要三步:
- 自定義需要發佈的事件類,需要繼承ApplicationEvent類
- 使用ApplicationEventPublisher來發佈自定義事件
- 使用@EventListener來監聽事件
這裡需要特別註意一點,默認情況下事件是同步的。即事件被publish後會等待Listener的處理。如果發佈事件處的業務存在事務,監聽器處理也會在相同的事務中。如果需要異步處理事件,可以onApplicationEvent方法上加@Aync支持異步或在有@EventListener的註解方法上加上@Aync。
源碼實戰
創建事件
public class ProductEvent extends ApplicationEvent { public ProductEvent(Product product) { super(product); } }
發佈事件
@Service public class ProductServiceImpl implements IproductService { ... @Autowired private ApplicationEventPublisher publisher; @Override @Transactional(rollbackFor = Exception.class) public void saveProduct(Product product) { productMapper.saveProduct(product); //事件發佈 publisher.publishEvent(product); } ... }
事件監聽
@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啟動類上增加@EnableAsync 註解
@Slf4j @EnableSwagger2 @SpringBootApplication @EnableAsync public class ApplicationBootstrap { ... }
使用瞭Async後會使用默認的線程池SimpleAsyncTaskExecutor,一般我們會在項目中自定義一個線程池。
@Configuration public class ExecutorConfig { /** 核心線程數 */ private int corePoolSize = 10; /** 最大線程數 */ private int maxPoolSize = 50; /** 隊列大小 */ private int queueCapacity = 10; /** 線程最大空閑時間 */ 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:當pool已經達到max size的時候,如何處理新任務 // CALLER_RUNS:不在新線程中執行任務,而是由調用者所在的線程來執行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
到此這篇關於SpringBoot實現異步事件驅動的方法的文章就介紹到這瞭,更多相關SpringBoot 異步事件驅動內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Spring的事件機制知識點詳解及實例分析
- SpringBoot事件發佈和監聽詳解
- SpringBoot中ApplicationEvent和ApplicationListener用法小結
- 詳解Spring事件發佈與監聽機制
- Springboot 如何使用@Async整合線程池