참조 : https://www.baeldung.com/spring-async
1. 개요
- Spring에서 제공하는 비동기 작업과 애노테이션 @Async에 대해서 알아본다.
- @Async를 Bean의 메서드에 선언하는 것만으로 해당 메서드는 별도의 쓰레드에서 수행된다. 즉, @Async가 선언된 메서드를 호출하는 객체는 메서드가 끝날 때까지 기다리지 않는다.
2. Async 옵션 켜기
- @Configuration과 @EnableAsync를 사용하여 Async 옵션을 켤 수 있다.
@Configuration
@EnableAsync
public class SpringAsyncConfig {
// ...
}
- @EnableAsync의 속성 부여를 통해 다양한 설정이 가능하다.
- annotation : @EnableAsync는 Default로 스프링의 @Async과 EJB 3.1의 javax.ejb.Asynchronous를 탐색한다. 이 옵션을 변경하여 다른 것을 탐색하도록 할 수 있다.
- mode : 사용할 advice의 타입을 지정한다. (JDK 프록시 기반 혹은 AspectJ weaving을 통해)
- proxyTargetClass : 사용할 프록시 타입을 지정한다. (CGLib 혹은 JDK를 통해) 이 속성은 mode가 AdviceMode.PROXY로 지정되었을 때만 작용된다.
- order : AsyncAnnotationBeanPostProcessor가 적용되어야 할 순서를 설정할 수 있다. 기본값으로, 존재하는 모든 프록시 객체를 고려하여 맨 마지막에 적용된다.
3. 애노테이션 @Async
- 애노테이션 @Async에는 두가지 제한점이 있다. 애노테이션이 지정되는 순간, 프록시 객체 형태로 수행되기 떄문이다.
- 제한점 1 : public 메서드에서만 적용 가능하다.
- 제한점 2 : 동일한 클래스 내에서 호출 시, 비동기 호출이 불가하다.
3.1 아무것도 반환하지 않는 메서드
@Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. " + Thread.currentThread().getName());
}
3.2 반환값이 있는 메서드
- @Async가 지정된 메서드는 Future로 감싼 값을 반환할 수 있다.
@Async
public Future<String> asyncMethodWithReturnType() {
System.out.println("Execute method asynchronously. " + Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult<String>("hello world");
} catch (InterruptedException ignored) {
}
return null;
}
- Spring은 Future를 구현한 클래스 AsyncResult를 제공하여, 비동기 메서드 호출의 결과를 추적할 수 있다.
public void testAsyncAnnotationForMethodsWithReturnType()
throws InterruptedException, ExecutionException {
System.out.println("Invoking an asynchronous method. " + Thread.currentThread().getName());
Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();
while (true) {
if (future.isDone()) {
System.out.println("Invoking an asynchronous method. " + Thread.currentThread().getName());
break;
}
System.out.println("Result from asynchronous process - " + future.get());
Thread.sleep(1000);
}
}
4. 비동기 메서드의 Executor
- 기본적으로, 스프링은 비동기 메서드를 실행할 때 SimpleAsyncTaskExecutor를 사용한다. 그러나 다른 Executor를 사용하도록 설정할 수 있다.
4.1 메서드 레벨에서 Executor 설정 변경하기
@Configuration
@EnableAsync
public class SpringAsyncConfig {
@Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
System.out.println("Execute method with configured executor - " + Thread.currentThread().getName());
}
4.2 애플리케이션 레벨에서 Executor 설정 변경하기
- Config 클래스에 AsyncConfigurer를 구현하고, 오버라이드된 getAsyncExecutor()에 변경하고자 하는 Executor를 반환하면 된다.
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
}
5. Exception Handling
- 반환 타입이 Future인 Async 메서드 실행 중에 Exception 발생 시, Future.get()메서드가 Exception을 던지게 된다.
- 만일 Async 메서드의 반환 타입이 void라면, Async 메서드 호출하는 곳으로 Exception이 전파되지 않는다. 이 경우에는 추가 설정을 통해 Exception을 처리해야 한다.
- 아래와 같이, 커스텀 Exception Handler 클래스를 생성하여야 한다. AsyncUncaughtExceptionHandler의 handleUncaughtException에 예외 처리 로직을 넣으면 된다.
- 클래스를 생성하여, AsyncConfig의 getAsyncUncaughtExceptionHandler()의 반환 값으로 넣어주면 된다.
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);
}
}
}
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
99. 더 읽어 볼 것
- https://www.baeldung.com/spring-events
- https://www.baeldung.com/spring-security-async-principal-propagation
- https://www.baeldung.com/spring-mvc-async-security
'Spring' 카테고리의 다른 글
[Spring] 서비스는 트랜잭션, 도메인의 순서 보장 역할만 제공한다 (0) | 2022.11.09 |
---|---|
[Spring] 트랜잭션 (0) | 2022.10.19 |
[Spring] 왜 Spring 인가 (0) | 2022.08.14 |
[Spring Batch] Tasklet & Chunk 예시 (0) | 2022.07.13 |