SpringBoot想必大家都用過,但是大家平時使用釋出的介面大都是同步的,那麼你知道如何優雅的實現非同步呢?
這篇文章就是關於如何在Spring Boot
中實現非同步行為的。但首先,讓我們看看同步和非同步之間的區別。
在Spring Boot
中,我們可以使用@Async
註解來實現非同步行為。
歡迎關注個人公眾號【JAVA旭陽】交流溝通
AsyncService.java
public interface AsyncService {
void asyncMethod() throws InterruptedException;
Future<String> futureMethod() throws InterruptedException;
}
AsyncServiceImpl.java
@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {
@Async
@Override
public void asyncMethod() throws InterruptedException {
Thread.sleep(3000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
}
@Async
@Override
public Future<String> futureMethod() throws InterruptedException {
Thread.sleep(5000);
log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName());
return new AsyncResult<>("task Done");
}
}
AsyncServiceImpl
是一個 spring
管理的 bean
。@Async
註解修飾。void
或 Future
。AsyncController.java
@EnableAsync
@RestController
@Slf4j
public class AsyncController {
@Autowired
AsyncService asyncService;
@GetMapping("/async")
public String asyncCallerMethod() throws InterruptedException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
asyncService.asyncMethod();
String response = "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
}
@GetMapping("/asyncFuture")
public String asyncFuture() throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
log.info("call async method, thread name: [{}]", Thread.currentThread().getName());
Future<String> future = asyncService.futureMethod();
// 阻塞獲取結果
String taskResult = future.get();
String response = taskResult + "task completes in :" +
(System.currentTimeMillis() - start) + "milliseconds";
return response;
}
}
@EnableAsync
,當然這個註解加在其他地方也ok得。asyncMethod()
將由預設任務執行程式建立的另一個執行緒執行,主執行緒不需要等待完成非同步方法執行。現在我們執行一下看看,是不是非同步返回的。
/async
介面,最終一步呼叫了方法。/asyncFuture
,發現返回5秒多,難道不是非同步的嗎?其實也是非同步的,看紀錄檔可以看出來,只不過我們返回的是Future
,呼叫Futrue.get()
是阻塞的。我們現在看看如果異常方法中報錯了會怎麼樣?修改非同步程式碼如下所示,會拋執行時異常:
再次執行非同步介面,如下所示,會使用預設的執行緒池和例外處理。
我們也可以自定義非同步方法的處理異常和非同步任務執行器,我們需要設定 AsyncUncaughtExceptionHandler
,如下程式碼所示:
@Configuration
public class AsynConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new
ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("asyn-task-thread-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler
getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable ex,
Method method, Object... params) {
System.out.println("Exception: " + ex.getMessage());
System.out.println("Method Name: " + method.getName());
ex.printStackTrace();
}
};
}
}
再次執行,得到的結果如下:
必須通過使用 @EnableAsync
註解註解主應用程式類或任何直接或間接非同步方法呼叫程式類來啟用非同步支援。主要通過代理模式實現,預設模式是 Proxy
,另一種是 AspectJ
。代理模式只允許通過代理攔截呼叫。永遠不要從定義它的同一個類呼叫非同步方法,它不會起作用。
當使用 @Async
對方法進行註解時,它會根據「proxyTargetClass
」屬性為該物件建立一個代理。當 spring
執行這個方法時,預設情況下它會搜尋關聯的執行緒池定義。上下文中唯一的 spring
框架 TaskExecutor bean
或名為「taskExecutor
」的 Executor bean
。如果這兩者都不可解析,預設會使用spring框架SimpleAsyncTaskExecutor
來處理非同步方法的執行。
在本文中,我們演示了在 spring boot 中如何使用 @Async
註解和非同步方法中的例外處理實現非同步行為。我們可以在一個介面中,需要存取不同的資源,比如非同步呼叫各個其他服務的介面,可以使用@Async
,然後將結果通過Future
的方式阻塞彙總,不失為一個提高效能的好方法。
歡迎關注個人公眾號【JAVA旭陽】交流溝通
本文來自部落格園,作者:JAVA旭陽,轉載請註明原文連結:https://www.cnblogs.com/alvinscript/p/17225659.html