成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Boot 實現方法異步調用的正確姿勢!

開發 前端
如果希望采用自定義線程池來執行,可以配置一個線程池對象并注入到 bean 工廠,最后在異步注解中指定即可;也可以全局配置默認線程池。

01、背景介紹

在實際的項目開發過程中,通常會碰到某個方法內各個邏輯并非緊密相連的業務。比如查詢文章詳情后更新文章閱讀量,其實對于用戶來說,最關心的是能快速獲取文章,至于更新文章閱讀量,用戶可能并不關心。

因此,對于這類邏輯并非緊密相連的業務,可以將邏輯進行拆分,讓用戶無需等待更新文章閱讀量,查詢時直接返回文章信息,縮短同步請求的耗時,進一步提升了用戶體驗。

要實現這種效果,很多同學可能立刻想到,采用異步線程來更新文章閱讀量。

是的,這個思路沒錯,在 Java 項目中,我們可以開啟一個線程來實現方法異步執行。

如果是在 Spring Boot 工程中,該如何優雅的實現方法異步調用呢?

今天帶著這個問題,我們一起來學習一下如何在 Spring Boot 中實現方法的異步調用。

02、方案實踐

實際上,從 Spring 3.0 之后,在 Spring Framework 的 Spring Task 模塊中,提供了@Async注解,將其添加在方法上,就可以自動實現該方法的異步調用效果。

不過有一個前提,需要在啟動類或配置類加上@EnableAsync注解,以便使異步調用@Async注解生效。

2.1、異步調用簡單示例

以用戶查詢文章詳情后,異步更新文章閱讀量為例,我們來看一個簡單的應用示例。

2.1.1、service 層代碼
@Component
public class ArticleService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ArticleService.class);

    /**
     * 查詢文章信息
     * @return
     */
    public String queryArticle(){
        LOGGER.info("查詢文章信息...");
        return "hello world";
    }

    /**
     * 更新文章閱讀量
     * @return
     */
    @Async
    public void updateCount(){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LOGGER.info("更新文章閱讀量...");
    }
}
2.1.2、controller 層代碼
@RestController
public class UserController {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private ArticleService articleService;

    @RequestMapping("/query")
    public String query(){
        LOGGER.info("用戶請求開始");
        // 查詢文章
        String result = articleService.queryArticle();
        // 更新文章閱讀量
        articleService.updateCount();
        LOGGER.info("用戶請求結束");
        return result;
    }
}
2.1.3、啟動類或配置類添加 EnableAsync 注解
@EnableAsync
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
2.1.4、服務測試

最后啟動服務,在瀏覽器中向query接口方法發起請求,輸出結果如下:

圖片圖片

從日志上可以清晰的看到,當發起查詢文章請求的時候,結果立刻響應給了客戶端;其次,更新文章閱讀量的方法采用的是task-1線程來執行,并沒有阻塞主線程的執行,異步調用效果明顯。

2.2、自定義線程池執行異步方法

被@Async注解標注的方法,默認采用SimpleAsyncTaskExecutor線程池來執行。這個線程池有一個特點就是,每來一個請求任務就會創建一個線程去執行,如果系統不斷的創建線程,最終可能導致 CPU 和內存占用過高,引發OutOfMemoryError錯誤。

實際上,SimpleAsyncTaskExecutor并不是嚴格意義上的線程池,因為它達不到線程復用的效果。因此,在實際開發中,建議自定義線程池來執行異步方法。

實現步驟也很簡單,首先,注入自定義線程池對象到 Spring Bean 中;然后,在@Async注解中指定線程池,即可實現指定線程池來異步執行任務。

2.2.1、配置自定義線程池類
@Configuration
public class AsyncConfig {

    @Bean("customExecutor")
    public ThreadPoolTaskExecutor asyncOperationExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 設置核心線程數
        executor.setCorePoolSize(3);
        // 設置最大線程數
        executor.setMaxPoolSize(5);
        // 設置隊列大小
        executor.setQueueCapacity(Integer.MAX_VALUE);
        // 設置線程活躍時間(秒)
        executor.setKeepAliveSeconds(30);
        // 設置線程名前綴+分組名稱
        executor.setThreadNamePrefix("customThread-");
        executor.setThreadGroupName("customThreadGroup");
        // 所有任務結束后關閉線程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化
        executor.initialize();
        return executor;
    }
}
2.2.2、在方法注解上指定線程池

比如,將更新文章閱讀量的方法,改成customExecutor線程池來執行,在@Async注解上指定線程池即可。

@Async("customExecutor")
public void updateCount(){
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LOGGER.info("更新文章閱讀量...");
}
2.2.3、服務測試

最后啟動服務,重新發起請求,輸出結果如下:

圖片圖片

從日志上可以清晰的看到,更新方法采用了customThread-1線程來異步執行任務。

2.3、配置全局默認線程池

從上文中我們得知,被@Async注解標注的方法,默認采用SimpleAsyncTaskExecutor線程池來執行。

某些場景下,如果希望系統統一采用自定義配置線程池來執行任務,但是又不想在被@Async注解的方法上一個一個的去指定線程池,如何處理呢?

此時可以重寫AsyncConfigurer接口的getAsyncExecutor()方法,配置默認線程池。

實現也很簡單,示例如下!

2.3.1、自定義默認異步線程池
@Configuration
public class AsyncConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 設置核心線程數
        executor.setCorePoolSize(3);
        // 設置最大線程數
        executor.setMaxPoolSize(5);
        // 設置隊列大小
        executor.setQueueCapacity(Integer.MAX_VALUE);
        // 設置線程活躍時間(秒)
        executor.setKeepAliveSeconds(30);
        // 設置線程名前綴+分組名稱
        executor.setThreadNamePrefix("asyncThread-");
        executor.setThreadGroupName("asyncThreadGroup");
        // 所有任務結束后關閉線程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 初始化
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, obj) ->{
            System.out.println("異步調用,異常捕獲---------------------------------");
            System.out.println("Exception message - " + throwable.getMessage());
            System.out.println("Method name - " + method.getName());
            for (Object param : obj) {
                System.out.println("Parameter value - " + param);
            }
            System.out.println("異步調用,異常捕獲---------------------------------");
        };
    }
}
2.3.2、服務測試

將@Async注解中指定的線程池,最后啟動服務,重新發起請求,輸出結果如下:

從日志上可以清晰的看到,更新方法采用了asyncThread-1線程來異步執行任務。

03、遇到的一些坑

在使用@Async注解的時候,可能會失效,總結下來主要有以下幾個場景。

  • 場景一:異步方法使用static修飾,此時不會生效
  • 場景二:調用的異步方法,在同一個類中,此時不會生效。因為 Spring 在啟動掃描時會為其創建一個代理類,而同類調用時,還是調用本身的代理類的,所以還是同步調用
  • 場景三:異步類沒有使用@Component、@Service等注解,導致 spring 無法掃描到異步類,此時不會生效
  • 場景四:采用SpringBoot框架開發時,沒有在啟動類上添加@EnableAsync注解,此時不會生效

其次,關于事務機制的一些問題,直接在@Async方法上再標注@Transactional是會失效的,此時可以在方法內采用編程式事務方式來提交數據。但是,在@Async方法調用其它類的方法上標注的@Transactional注解有效。

04、小結

最后總結一下,在 Spring Boot 工程中,如果想要實現方法異步執行的效果,只需要兩步即可完成。

首先,在啟動類或者配置類上添加@EnableAsync,表達開啟異步執行功能;然后,在需要異步執行的方法上,添加@Async注解,使方法實現異步調用的目標。

如果希望采用自定義線程池來執行,可以配置一個線程池對象并注入到 bean 工廠,最后在異步注解中指定即可;也可以全局配置默認線程池。

示例代碼地址:

https://gitee.com/pzblogs/spring-boot-example-demo

責任編輯:武曉燕 來源: 潘志的研發筆記
相關推薦

2010-02-25 09:13:34

WCF異步調用

2025-03-11 00:55:00

Spring停機安全

2024-07-31 15:57:41

2024-10-15 10:28:43

2021-09-15 16:20:02

Spring BootFilterJava

2020-01-02 16:30:02

Spring BootJava異步請求

2022-09-27 12:01:56

Spring異步調用方式

2018-06-21 14:46:03

Spring Boot異步調用

2009-11-06 15:54:15

WCF異步調用

2009-07-01 14:37:14

JavaScript異

2009-07-01 14:23:46

JavaScript異

2022-09-28 14:54:07

Spring注解方式線程池

2024-08-01 09:10:03

2009-12-07 14:26:47

WCF異步調用

2009-10-20 16:48:30

C#委托

2009-08-21 11:24:16

C#異步調用

2025-02-12 08:07:40

2009-11-09 10:50:30

WCF異步調用

2009-12-21 14:10:26

WCF異步調用

2021-03-29 09:26:44

SpringBoot異步調用@Async
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产1区2区3区 | 日韩精品一区二区三区在线观看 | 国产激情自拍视频 | www.干| 中文字幕在线一区二区三区 | 国产精品久久久久久久久久免费 | 国产精品久久久久久久久久免费看 | chinese中国真实乱对白 | 国产一区二区三区亚洲 | 一区中文字幕 | 久久国产精品免费一区二区三区 | 国产成人精品一区二区三区 | 91精品久久久久久久久 | 久久伊人操 | 亚洲国产高清高潮精品美女 | 亚洲成人在线网 | 久久最新 | 在线中文字幕视频 | 亚洲精品三级 | 国产免费看 | 久久精品成人 | 视频1区| 日本免费小视频 | 欧美性久久| 久久一区二区av | 亚洲精品乱码久久久久久久久久 | 韩日精品一区 | 久久精品中文字幕 | 九色网址 | 国产成人啪免费观看软件 | 婷婷久久久久 | 欧美网址在线观看 | 国产精品成人国产乱 | av一区二区三区在线观看 | 日本特黄a级高清免费大片 特黄色一级毛片 | 天堂一区二区三区 | 欧美精品中文字幕久久二区 | 久久久夜夜夜 | 国产网站在线播放 | 精品九九 | a级大片免费观看 |