@configuration 中@value不生效_在SpringBoot中建立非同步介面
阿新 • • 發佈:2021-01-31
技術標籤:@configuration 中@value不生效
概要
我們常常需要在一個介面中實現比較耗時的操作,在執行一段時間後,將結果以callback的方式返回給呼叫方。又或者在一個介面中需要非同步執行多個操作,將結果彙總後返回給呼叫方。
主要物件
在上述的需求中,我們一般會涉及到這樣幾個物件:
- 任務:用來執行耗時的操作
- 執行者:用來執行任務,一般都是新開一個執行緒執行耗時任務
- SpringBoot上下文,用來管理各種Bean
SpringBoot提供了很方便的@EnableAsync和@Async註解來幫我們實現上述的需求。
- @Async 用來標註一個方法,讓SpringBoot框架可以用執行者(Executor)來非同步執行這個方法。
- @EnableAsync 這個註解是讓SpringBoot框架在啟動時開啟非同步執行這個能力。
程式碼講解
配置類
@[email protected] //①public class AsyncConfiguration { @Bean(name = "asyncExecutor") //② public Executor asyncExecutor() { //③ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3); executor.setMaxPoolSize(3); executor.setQueueCapacity(100); executor.setThreadNamePrefix("AsyncThread-"); executor.initialize(); return executor; //③ }}
① 如上文所述,我們啟用非同步處理這個特性
② 為了配置自定義的執行者(Executor),我們需要建立一個 Bean,並且為這個執行者取一個名字,後面會用到這個名字。
③ 建立一個自定義的執行緒池,用做執行者(Executor)。
注意:如果不想調整執行的執行緒池,可以只寫一個空的AsyncConfiguration類就可以
如果你還想自定義異常處理(可選)
@[email protected] class AppConfig implements AsyncConfigurer { //① @Override public Executor getAsyncExecutor() { //② ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(7); executor.setMaxPoolSize(42); executor.setQueueCapacity(11); executor.setThreadNamePrefix("MyExecutor-"); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { //③ return new MyAsyncUncaughtExceptionHandler(); } }
① 需要繼承 AsyncConfigurer
② 過載 getAsyncExecutor() 定製執行者
③ 過載 getAsyncUncaughtExceptionHandler() 定製異常處理部分
任務類
@Service //①@Slf4jpublic class SampleAsyncService { @Async(value = "asyncExecutor") //② public void doAsyncTask() throws InterruptedException { Thread.sleep(ThreadLocalRandom.current().nextInt(5000)); log.info("Do Async Task...=END="); }}
① @Async需要在一個獨立的Bean中才可以生效。這裡我們使用Service(別說你不知道Service也是一種Bean)。
② 這裡設定的值就是執行者(Executor)的名字
在Controller中使用
@GetMapping("doasynctask") public String doAsyncTask() throws InterruptedException { log.info("Do asyncTask ..."); sampleAsyncService.doAsyncTask(); //① log.info("do async task"); return "success"; }
① 呼叫方法,SpringBoot會自動以非同步的方式執行
測試一下,可以發現任務非同步執行了。
2020-01-03 12:42:25.916 INFO 38145 --- [nio-8080-exec-1] c.m.m.c.MyFirstController : Do asyncTask ...2020-01-03 12:42:25.916 INFO 38145 --- [nio-8080-exec-1] c.m.m.c.MyFirstController : do async task2020-01-03 12:42:30.071 INFO 38145 --- [ AsyncThread-2] c.m.m.s.SampleAsyncService : Do Async Task...=END=
如果你需要在介面中等待多個操作的結果
首先,需要改寫一下任務類
@Async(value = "asyncExecutor") public CompletableFuture doAsyncTaskWithName(String name) throws InterruptedException { Thread.sleep(ThreadLocalRandom.current().nextInt(5000)); log.info("Do Async Task...=END="); return CompletableFuture.completedFuture(name); //① 返回 }
① 需要返回非同步的物件
之後,需要實現我們的介面,在這個介面中,我們等待多個任務執行的結果,然後統一輸出
@GetMapping("doAsyncTaskWithName") public String doAsyncTaskWithName() throws InterruptedException, ExecutionException { log.info("Do asyncTask ..."); CompletableFuture result1 = sampleAsyncService.doAsyncTaskWithName("task1"); CompletableFuture result2 = sampleAsyncService.doAsyncTaskWithName("task2"); CompletableFuture result3 = sampleAsyncService.doAsyncTaskWithName("task3"); log.info("do async task"); CompletableFuture.allOf(result1, result2, result3).join(); //① log.info("doAsyncTaskWithName1: " + result1.get()); log.info("doAsyncTaskWithName2: " + result2.get()); log.info("doAsyncTaskWithName3: " + result3.get()); return "success"; }
① 這裡我們等待所有非同步任務執行完畢。