1. 程式人生 > 其它 >@configuration 中@value不生效_在SpringBoot中建立非同步介面

@configuration 中@value不生效_在SpringBoot中建立非同步介面

技術標籤:@configuration 中@value不生效

8a74485b825388e29a38663f999a4ec9.png

概要

我們常常需要在一個介面中實現比較耗時的操作,在執行一段時間後,將結果以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";    }

① 這裡我們等待所有非同步任務執行完畢。