스프링에서 편리하게 비동기 기능을 사용하기 위해 @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
프록시를
호출할것입니다.
이제 쓰레드가 분리되어 실행되는것을 확인할 수 있습니다.
'SPRING' 카테고리의 다른 글
[SPRING] Spring Boot 3.x queryDsl Gradle 설정 (0) | 2024.09.14 |
---|---|
[SPRING] Spring Boot 3.0 + Springdoc(Swagger3) 404에러 (0) | 2023.01.03 |
[SPRING] 환경별 application.properties 관리 (0) | 2022.12.24 |
[SPRING] @WebMvcTest의 Controller테스트 (0) | 2022.09.18 |
[SPRING] HttpServletRequest InputStream의 주의사항 (0) | 2022.08.01 |
댓글