본문 바로가기
SPRING

[SPRING] @Async 동작하지 않는 상황

by steadyMan 2023. 8. 25.

스프링에서 편리하게 비동기 기능을 사용하기 위해 @Async 애노테이션을 사용합니다.

이전에 기능변경으로 비동기 코드를 추가 했으나 적용이 되지 않았던 상황이 있었고 원인은 @Async의 동작방식을

정확히 알지 못하고 사용해서 발생한 상황이였습니다.


상황 예시

@Slf4j
@Component
public class AsyncClass {
    @Async
    public void outerMethod()  {
        log.info("outerMethod START");
        innerMethod();
        log.info("outerMethod END");
    }
    
    @Async
    public void innerMethod()  {
        log.info("innerMethod START");
        log.info("innerMethod END");
    }
}

outerMethod()의 코드에서 innerMethod() 도 비동기로 실행되는게 목표였습니다.

하지만 실행결과는

이 처럼 하나의 쓰레드에서 전부 실행되는 즉 비동기처리가 되지 않았습니다.

원인

원인은 아주 간단했습니다.

@Async는 스프링 AOP에 의해 프록시 패턴 기반으로 동작하기 때문에 정상적으로 실행되지 않았던 것입니다.

AsyncClass의 프록시가 컨테이너에 올라가 있고 outerMethod()가 호출되었을때 프록시에서 실제 타겟인

AsyncClass가 호출되고 outerMethod() 안에서 실행되는 innerMethod()는 실제 타켓인 AsyncClass에서

호출 되기 때문에 비동기로  실행되지 않은 것입니다.

 

정말 프록시 기반으로 실행되는지 확인해보겠습니다.

해당 코드의 콜스택을 보면 AsyncClass가 CGLIB 프록시로 생성되어 있는것을 확인할 수 있습니다.

해결방법

이런 상황에 정통적인 방법인 클래스를 분리하여 비동기가 정상적으로 실행되는지 확인해보겠습니다.

@Slf4j
@Component
@RequiredArgsConstructor
public class AsyncClass {

    private final _AsyncClass _asyncClass;
    
    @Async
    public void outerMethod()  {
        log.info("outerMethod START");
        _asyncClass.innerMethod();
        log.info("outerMethod END");
    }
}

기존에 innerMethod()를 _AsyncClass로 분리하였습니다. 이렇게 되면 outerMethod()는 _AsyncClass 프록시를

호출할것입니다.

이제 쓰레드가 분리되어 실행되는것을 확인할 수 있습니다.

댓글