TODAY 4
ThreadPoolTaskExecutor

@Async 어노테이션을 bean에 넣으면 별도의 쓰레드에서 실행되는 것이다.

호출자는 호출된 메소드가 완료될 때까지 기다릴 필요가 없다.

Enable Async Support

@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }

기본적인 설정은 위와 같다.
추가적인 어노테이션은 아래와 같다.

(1) annotation
- 기본, @EnableAsync 어노테이션은 @Async 어노테이션을 감지한다.
- EJB 3.1 javax.ejb.Asynchronous, 이 옵션으로 다른 옵션과 어노테이션, 사용자 정의 어노테이션을 감지할수 있다.
(2) Mode
- advice의 타입입이 JDK proxy-based 인지 AspectJ 인지 나타낸다.
(3) proxyTargetClass
- 프록시 데이터 타입이 CGLIB 또는 JDK 인지 알려준다.
- MODE 가 "AdviceMode.PROXY" 상태일떄만 이 설정이 유효하다.
(4) order
-  AsyncAnnotationBeanPostProcessor 의 순서를 설정합니다.

비동기 처리는 XML 을 이용해서도 할수 있다.
- namespace : task


@Async 어노테이션
두가지 룰이 있다.
- Public 메소드만 적용해야 한다.
- self-invocation(동일 메서드 안에서 Async 호출)은 작동하지 않는다.
 이유는 메소드가 Public 이여야 프록시가 될수 있고, 셀프 호출을 하게되면 프록시를 위회하게 되므로 작동하지 않는다.

리턴이 Void 타입인 메소드

@Async
public void asyncMethodWithVoidReturnType() {
    System.out.println("Execute method asynchronously. "
      + Thread.currentThread().getName());
}

리턴타입이 있는 메소드 - Future객체에 실제 값을 넣음으로서 @Async 는 리턴이 존재하는 메소드 붙일수 있다,

@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 e) {
        //
    }
    return null;
}

스프링은 또한 Future를 구현한 AsyncResult 클래스를 제공하며 이는 비동기 메소드 실행의 결과를 가져오는데 사용한다.

이제 위의 메소드를 호출하여 Future 객체를 사용해 비동기 처리의 결과값을 가져와보자.

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("Result from asynchronous process - " + future.get());
            break;
        }
        System.out.println("Continue doing something else. ");
        Thread.sleep(1000);
    }
}

 

실행자 ( The Executor )
스프링은 SimpleAsyncTaskExecutor 를 사용하여 실제로 비동기적으로 메소드들을 실행한다.
기본적으로 어플리케이션 레벨 또는 개인 메소드레벨 두가지로 오버라이드 되어 레벨을 정할수 있다.

메소드 레벨로 실행자 오버라이드 하기
설정 클래스에서 필요한 실행자를 선언해야 한다.

@Configuration
@EnableAsync
public class SpringAsyncConfig {
     
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}

그후 실행자의 이름을 @Async에서 속성값으로 제공 해주어야 한다.

@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
    System.out.println("Execute method with configured executor - "
      + Thread.currentThread().getName());
}

어플리케이션 레벨로 실행자 오버라이드 하기
이 경우 설정 클라스는 AsyncConfigurer 인터페이스를 implement(구현)해야한다.
이렇게 하게 되면 getAsyncExecutor() 메소드를 implement(구현) 해야한다. 여기서 우리는 전체 어플리케이션을 위한 실행자를 리턴할 것이다.
이제 @Async로 어노테이션된 메소드를 실행하는 기본 실행자가 된다.

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
     
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}

 

예외 핸들링
메소드의 리턴타입이 Future 인경우 핸들링 하기 쉽다. Future.get()로 이셉션을 받을수 있다.
그렇지만, 리턴 타입이 void 인 경우 예외는 호출 스레드에 전달되지 않습니다. 따라서 우리는 예외 처리에 필요한 추가 구성이 필요합니다.
우리는 AsyncUncaughtExceptionHandler 인터페이스를 구현함으로서 커스텀 비동기 예외처리자를 만들것이다.
handleUncaughtException() 메소드는 잡히지않은uncaught 비동기 예외가 발생할때 호출됩니다.

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);
        }
    }
     
}

전 섹션에서 우리는 설정 클래스에 의해 구현된 AsyncConfigurer 인터페이스를 보았다.
그 일부로서 우리의 커스텀 비동기 예외처리자를 리턴하는 getAsyncUncaughtExceptionHandler() 메소드 또한 오버라이드해주어야한다.

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new CustomAsyncExceptionHandler();
}

 

참고 URL : http://www.baeldung.com/spring-async 
참고 URL : https://github.com/eugenp/tutorials/tree/master/spring-all