@Async 어노테이션과 약간의 Spring AOP 개념

Kyu·2023년 1월 9일
1

Spring 공부기록

목록 보기
10/10

📌 스프링 @Async 에 대해서 알아봅시다. 약간의 AOP 배경지식도 필요합니다.


Spring AOP

스프링이 비동기 작업을 수행하기 위해선
기본적으로 런타임에서 해당 클래스에 대한 프록시가 필요하다.

용어

  • Aspect: 여러 클래스를 cut across 하는 관심사의 모듈화
    • 예를 들면, Transactional 어노테이션을 생각하면 됨
  • Joinpoint: 프로그램 실행 시점, 메소드의 진입지점이라고 생각하면 됨
  • Advice: 특정 join point 에서 aspect 에 의해 취해진 액션
    • 다양한 advice type 이 있음, 가령 "around", "before", "after" ...
    • 스프링 포함 많은 AOP 프레임웤에서 Advice 를 인터셉터로 모델링하고, 인터셉터 체인을 관리한다.
  • Pointcut: Joint point 의 정규 표현식, Join point 가 Pointcut에 일치할때마다 해당 Pointcut에 관련된 Advice가 실행된다.

자세히 알아보기:

@Async 어노테이션

Executor 를 커스텀으로 빈으로 등록하지 않았으면,
Spring 이 자동으로 ThreadPoolTaskExecutor 를 기본값과 함께 컨테이너에 빈을 등록한다ThreadPoolTaskExecutor 은 @EnableAsync 와 Spring MVC 비동기 요청 프로세스를 동작하도록 해준다.

요약

  • 호출되어진 스레드의 로직은 호출한 스레드에 영향을 안준다.
    • Simply put, annotating a method of a bean with @Async will make it execute in a separate thread. In other words, the caller will not wait for the completion of the called method. - https://www.baeldung.com/spring-async
  • 메소드를 비동기 실행을 할 수 있도록 마크한다.
    • type 레벨에도 사용할 수 있다.
      • 이 경우엔 모든 메소드가 async 로 동작한다
  • @Configuration 이 붙은 어노테이션 밑에 @EnableAsync반드시 넣어야한다.
  • @Async 메소드 시그니쳐에는 어떤 파라미터 타입이 들어와도 상관없다.
  • 리턴타입은 void 혹은 Futrue 이어야한다.
    • Future
      • CompletableFuture 같은 것을 사용하면 비동기된 메소드를 기다려 리턴값을 받을 수 있다.

더 알아보기:

실습하면서 얻은 정보:

  • private 메소드이면 안된다.
    • why?
      • public 이어야 프록시생성 할 수 있기 때문이다
  • 동일 클래스에서 호출하면 안된다
    • why?
      • self-invocation doesn't work because it bypasses the proxy and calls the underlying method directly.
  • 클래스 내부에 Proxy 기반 동작하는 어노테이션 (@Transactional, @Async, @Cacheable, 커스텀어노테이션 등) 메소드가 있으면, 일반 bean 이 아니라 Proxy 로 래핑되서 bean 이 생성된다.
    • Spring Context 가 Bean 후보들을 스캔하여 Bean 으로 생성할때 래핑하냐 마냐를 결정한다. 빈이 생성되는 과정에서 AbstractAutoProxyCreator.wrapIfNecessary() 함수가 호출되는데 이름에서 알 수 있듯이 필요한 경우에 Bean 으로 만들고자 하는 클래스를 Proxy 로 Wrap 하는 역할을 수행하는 함수이다.

트러블 슈팅

아래 순서대로 호출했는데 @Async 가 동작하지 않음

1. Controller 에서 서비스(bean) 객체 메소드 호출 
2. 내부 private 메소드 호출 
3. 다른 서비스(bean) `@Async` 메소드 호출
  • 위 순서를 테스트하면 정상적으로 async 가 동작하지만 실무에서 동작하지 않는 문제가 있었음.
  • @Async 가 실행 되지 않는 원인은 해당 메소드의 클래스의 프록시 객체가 만들어지지 않았기 때문이다.
    • 원인은 아직도 모르겠음
  • 해당 클래스는 서비스 클래스이기 때문에 어찌됐던 간에 프록시를 만들어 사용하면 해결되었다.
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
  • 위 어노테이션을 붙이면 CGLIB 프록시 객체가 만들어진다.
profile
TIL 남기는 공간입니다

2개의 댓글

comment-user-thumbnail
2023년 1월 9일

링크에 대한 요약도 적어주셔서 읽기 좋았습니다. 좋은 글 감사합니다 Kyu !

1개의 답글