樓主在前面的2篇文章中,分別介紹了Java子線程中通用的異常處理,以及Spring web應用中的異常處理。鏈接如下:
今天,要寫的是被Spring @Async註解的方法中的異常處理方法。
通常,如果我們要在程序中做一個耗時的操作(例如調用其他外部模塊),一般會通過異步的方式執行。
有這2種方法:
- 自行生成線程池ThreadPoolExecutor,提交任務執行
- 更方便地,使用Spring @Async註解,修飾在需要異步執行的方法上
對於第一種方法的異常處理,樓主已經在“Java子線程中的異常處理(通用)”這篇文章中介紹了,也就是提交任務後獲取到Future對象,通過future.get()獲取返回值的時候能夠捕獲到ExcecutionException。
對於Spring @Async註解的方法,如何進行異常處理呢?樓主想到了2種方法。
解決辦法
方法一:配置AsyncUncaughtExceptionHandler(對於無返回值的方法)
通過AsyncConfigurer自定義線程池,以及異常處理。
1 @Configuration
2 @EnableAsync
3 public class SpringAsyncConfiguration implements AsyncConfigurer {
4 private static final Logger logger = LoggerFactory.getLogger(getClass());
5 @Bean
6 @Override
7 public Executor getAsyncExecutor() {
8 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
9 executor.setCorePoolSize(8);
10 executor.setMaxPoolSize(16);
11 executor.setQueueCapacity(64);
12 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
13 executor.setThreadNamePrefix("SpringAsyncThread-");
14
15 return executor;
16 }
17
18 @Override
19 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
20 return new SpringAsyncExceptionHandler();
21 }
22
23 class SpringAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
24 @Override
25 public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
26 logger.error("Exception occurs in async method", throwable.getMessage());
27 }
28 }
29
30 }
方法二:通過AsyncResult捕獲異常(對於有返回值的方法)
如果異步方法有返回值,那就應當返回AsyncResult類的對象,以便在調用處捕獲異常。
因爲AsyncResult是Future接口的子類,所以也可以通過future.get()獲取返回值的時候捕獲ExcecutionException。
異步方法:
@Service
public class AsyncService {
@Async
public AsyncResult<String> asyncMethodWithResult() {
// do something(可能發生異常)
return new AsyncResult("hello");
}
}
調用處捕獲異常:
1 public class Test {
2
3 private Logger logger = LoggerFactory.getLogger(getClass());
4
5 @Autowired
6 AsyncService asyncService;
7
8 public void test() {
9 try {
10 Future future = asyncService.asyncMethodWithResult();
11 future.get();
12 } catch (ExecutionException e) {
13 logger.error("exception occurs", e);
14 } catch (InterruptedException e) {
15 logger.error("exception occurs", e);
16 }
17 }
18
19 }
@Async調用中的事務處理機制
在@Async標註的方法,同時也適用了@Transactional進行了標註;在其調用數據庫操作之時,將無法產生事務管理的控制,原因就在於其是基於異步處理的操作。
那該如何給這些操作添加事務管理呢?可以將需要事務管理操作的方法放置到異步方法內部,在內部被調用的方法上添加@Transactional.
例如: 方法A,使用了@Async/@Transactional來標註,但是無法產生事務控制的目的。
方法B,使用了@Async來標註, B中調用了C、D,C/D分別使用@Transactional做了標註,則可實現事務控制的目的。