例子翻譯自國外的兩篇部落格:
https://www。baeldung。com/spring-async
https://spring。io/guides/gs/async-method/
實驗環境準備
JDK 1。8
SpringBoot2。2。1
Maven 3。2+
開發工具
IntelliJ IDEA
smartGit
建立一個SpringBoot Initialize專案,詳情可以參考我之前部落格:SpringBoot系列之快速建立專案教程
pom。xml:
<?xml version=“1。0” encoding=“UTF-8”?>
github使用者資訊類
@JsonIgnoreProperties(ignoreUnknown = true),將這個註解寫在類上之後,就會忽略類中不存在的欄位,可以滿足當前的需要。
package com。example。springboot。async。bean;import com。fasterxml。jackson。annotation。JsonIgnore;import com。fasterxml。jackson。annotation。JsonIgnoreProperties;import lombok。Data;import java。io。Serializable;/** *
* 使用者資訊實體類 * Copy @ https://spring。io/guides/gs/async-method/ ** *
* 修改記錄 * 修改後版本: 修改人: 修改日期: 2020/07/20 10:14 修改內容: **/@JsonIgnoreProperties(ignoreUnknown = true)@Datapublic class User implements Serializable { private String name; private String blog; @Override public String toString() { return “User{” + “name=‘” + name + ’\‘’ + “, blog=‘” + blog + ’\‘’ + ‘}’; }}
非同步任務配置類
可以實現AsyncConfigurerSupport 類,也可以使用@Bean(name = “threadPoolTaskExecutor”)的方法,這裡定義了執行緒池的配置
package com。example。springboot。async。config;import com。example。springboot。async。exception。CustomAsyncExceptionHandler;import org。springframework。aop。interceptor。AsyncUncaughtExceptionHandler;import org。springframework。context。annotation。Bean;import org。springframework。context。annotation。Configuration;import org。springframework。scheduling。annotation。AsyncConfigurer;import org。springframework。scheduling。annotation。AsyncConfigurerSupport;import org。springframework。scheduling。annotation。EnableAsync;import org。springframework。scheduling。concurrent。ThreadPoolTaskExecutor;import java。util。concurrent。Executor;/** *
* AsyncConfiguration * Copy @https://spring。io/guides/gs/async-method/ ** *
* 修改記錄 * 修改後版本: 修改人: 修改日期: 2020/07/20 10:12 修改內容: **/@Configuration@EnableAsyncpublic class AsyncConfiguration extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor。setCorePoolSize(2); executor。setMaxPoolSize(2); executor。setQueueCapacity(500); executor。setThreadNamePrefix(“GithubLookup-”); executor。initialize(); return executor; } /*@Bean(name = “threadPoolTaskExecutor”) public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); }*/ }
查詢github使用者資訊業務類
使用Future獲得非同步執行結果時,要麼呼叫阻塞方法get(),要麼輪詢看isDone()是否為true,這兩種方法都不是很好,因為主執行緒也會被迫等待。
在Java8中,CompletableFuture提供了非常強大的Future的擴充套件功能,可以幫助我們簡化非同步程式設計的複雜性,並且提供了函數語言程式設計的能力,可以透過回撥的方式處理計算結果,也提供了轉換和組合 CompletableFuture 的方法。
package com。example。springboot。async。service;import com。example。springboot。async。bean。User;import org。slf4j。Logger;import org。slf4j。LoggerFactory;import org。springframework。boot。web。client。RestTemplateBuilder;import org。springframework。scheduling。annotation。Async;import org。springframework。stereotype。Service;import org。springframework。web。client。RestTemplate;import java。util。concurrent。CompletableFuture;import java。util。concurrent。Future;/** *
* GitHubLookupService * copy @https://spring。io/guides/gs/async-method/ ** *
* 修改記錄 * 修改後版本: 修改人: 修改日期: 2020/07/20 10:18 修改內容: **/@Servicepublic class GitHubLookupService { private static final Logger LOG = LoggerFactory。getLogger(GitHubLookupService。class); private final RestTemplate restTemplate; public GitHubLookupService(RestTemplateBuilder restTemplateBuilder) { this。restTemplate = restTemplateBuilder。build(); } @Async //@Async(“threadPoolTaskExecutor”) public Future
啟動測試類實現
實現CommandLineRunner 介面,SpringBoot啟動時候,會自動呼叫,也可以用@Order指定執行順序
package com。example。springboot。async。service;import com。example。springboot。async。bean。User;import org。slf4j。Logger;import org。slf4j。LoggerFactory;import org。springframework。beans。factory。annotation。Autowired;import org。springframework。boot。CommandLineRunner;import org。springframework。stereotype。Component;import java。util。concurrent。Future;/** *
* CommandLineRunner * Copy @https://spring。io/guides/gs/async-method/ ** *
* 修改記錄 * 修改後版本: 修改人: 修改日期: 2020/07/20 10:25 修改內容: **/@Componentpublic class AppRunner implements CommandLineRunner { private static final Logger logger = LoggerFactory。getLogger(AppRunner。class); @Autowired GitHubLookupService gitHubLookupService; @Override public void run(String。。。 args) throws Exception { // Start the clock long start = System。currentTimeMillis(); // Kick of multiple, asynchronous lookups Future
自定義非同步任務異常
當方法返回型別為Future時,異常處理很容易– Future。get()方法將引發異常。但是,如果返回型別為void,則異常不會傳播到呼叫執行緒。因此,我們需要新增額外的配置來處理異常。我們將透過實現AsyncUncaughtExceptionHandler介面來建立自定義非同步異常處理程式。
package com。example。springboot。async。exception;import org。springframework。aop。interceptor。AsyncUncaughtExceptionHandler;import java。lang。reflect。Method;/** *
* Copy @ https://www。baeldung。com/spring-async ** *
* 修改記錄 * 修改後版本: 修改人: 修改日期: 2020/07/20 11:08 修改內容: **/public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException( Throwable throwable, Method method, Object。。。 obj) { System。out。println(“Exception message - ” + throwable。getMessage()); System。out。println(“Method name - ” + method。getName()); for (Object param : obj) { System。out。println(“Parameter value - ” + param); } }}
需要重寫getAsyncUncaughtExceptionHandler()方法以返回我們的自定義非同步異常處理程式
@Configuration@EnableAsyncpublic class AsyncConfiguration extends AsyncConfigurerSupport { // 。。。 @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); }}
啟動之後,可以看到如下資訊,因為是多執行緒方式,所以都不是在main執行緒裡的,非同步執行的
在這裡插入圖片描述
如果註釋@Async註解,再次啟動,會發現都在main主執行緒裡執行程式