ilot
ilot의 블로그
ilot
전체 방문자
오늘
어제
  • 분류 전체보기 (17)
    • Algorithm (0)
    • Data Structure (0)
    • Database (0)
    • Operating System (0)
    • Network (0)
    • OOP (1)
    • Design Pattern (5)
    • Java (2)
    • Spring (5)
    • Mybatis (1)
    • JavaScript & TypeScript (0)
    • React (0)
    • Coding Test (2)
    • 독후감 (1)
    • 일상 (0)

블로그 메뉴

  • 홈
  • Github

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
ilot

ilot의 블로그

Spring

[Spring] Async

2022. 10. 28. 23:05
참조 : 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
    'Spring' 카테고리의 다른 글
    • [Spring] 서비스는 트랜잭션, 도메인의 순서 보장 역할만 제공한다
    • [Spring] 트랜잭션
    • [Spring] 왜 Spring 인가
    • [Spring Batch] Tasklet & Chunk 예시
    ilot
    ilot
    _

    티스토리툴바