비동기 메서드 호출시 예외를 던졌는데 @ControllerAdvice에서 catch 하지 못했다. 찾아보니.. 오직 동기적 예외만 잡을 수 있단다..
https://stackoverflow.com/questions/44138199/spring-exceptionhandler-and-multi-threading
https://www.baeldung.com/spring-async
비동기 예외처리는 다른 전용 인터페이스가 있단다..
반환 유형이 void 이면 예외가 호출 스레드로 전파되지 않습니다 .
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);
}
}
}
@EnableAsync
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setThreadNamePrefix("kang-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
@Bean
public ThreadPoolTaskScheduler configureTasks() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("kang-scheduled-");
threadPoolTaskScheduler.initialize();
return threadPoolTaskScheduler;
}
}
@Async// 분석요청 aysnc
public void mockInsUpload() throws Exception {
log.info("#### mockCrescomAiService.insUpload() ");
String apiUrl = "http://34.64.157.45:80/ins_upload";
Map<String, Object> resultMap = new HashMap<>();
FileInputStream fis = new FileInputStream("src/main/resources/img/testImg.txt");
String data = IOUtils.toString(fis, "UTF-8");
log.info("읽어들인 파일: " + data);
Map<String, String> header = new HashMap<String, String>();
// header.put("Content-Type", "multipart/form-data");
header.put("Content-Type", "application/x-www-form-urlencoded");
Map<String, Object> params = new HashMap<String, Object>();
//params.put("id", "AIBAA"); // oscID
params.put("fix_id", "korea_uni_hos"); // 고객ID
HttpResponse<JsonNode> response = Unirest.post(apiUrl)
.headers(header)
.queryString(params)
.asJson();
// Future<HttpResponse<JsonNode>> future = Unirest.post(apiUrl)
// .headers(header)
// .queryString(params)
// .asJsonAsync(new Callback<JsonNode>() {
// @Override
// public void completed(HttpResponse<JsonNode> response) {
// System.out.println(response.getBody().getObject());
// glovar.synchronizedList.remove(glovar.synchronizedList.size()-1);
// //glovar.target = glovar.synchronizedList.size();
// System.out.println("토탈개수: " + glovar.target);
// System.out.println("완료되면 제거: " + glovar.synchronizedList.toString() + " " + (glovar.target - glovar.synchronizedList.size()));
//
//
// }
//
// @Override
// public void failed(UnirestException e) {
// System.out.println("data 통신 실패");
// }
//
// @Override
// public void cancelled() {
// System.out.println("data 통신 취소");
// }
//
// });
JSONObject responseJson = response.getBody().getObject();
log.info("## CrescomAiService.mockInsUpload() responseJson={}", responseJson);
//
// String jsonString = responseJson.toString();
// Map<String, Object> map = new ObjectMapper().readValue(jsonString, new TypeReference<Map<String, Object>>(){});
// log.info("## CrescomAiService.mockinsUpload() map={}", map);
//
// resultMap.put("result", "성공"); //원래는 무슨 응답을 보냈는지 알 수 가 없는데
// resultMap.put("code", "0000");
if (responseJson.has("error")) {
// 중복 로그인 방지
throw new CallApiException("API call Exception");
}
//return CompletableFuture.completedFuture(resultMap);
}
아래 방법도 있는듯..
메소드 리턴 유형이 Future 인 경우 예외 처리가 용이합니다. Future.get () 메소드는 예외를 처리 합니다.
@Controller
public class ApiController {
private final Service service;
public ApiController(Service service) {
this.service = service;
}
@GetMapping( "/activate")
public CompletableFuture<Void> activate(...){
return service.activateUser(...)
.exceptionally(throwable -> ... exception handling goes here ...)
}
}
@Service
public class Service {
@Async
public CompletableFuture<Void> activateUser(...) {
CompletableFuture<Void> future = new CompletableFuture<>();
... your code goes here ...
if(someCondition){
future.completeExceptionally(new GeneralSecurityException());
} else {
future.complete(null);
}
return future;
}
}