Handling Circular dependency

겔로그·2023년 9월 17일
0
post-thumbnail

이 글은 Top Difficult Java Spring Boot Problems Every Developer Should Know: Unraveling the Circular Dependency Dilemma를 읽고 정리한 글입니다.

Circular dependency

Circular dependency는 Spring Boot Framework의 의존성 주입(Dependency Injection(DI)) 지원이라는 특징으로 인해 빈번하게 발생하는 문제입니다.

Spring Boot에서는 Bean이라는 개념을 이용해 의존성 주입(Dependency Injection(DI))을 지원하는데 다음과 같은 상황에서 Circular dependency가 발생합니다.

Circular dependency가 발생하는 경우

  • A가 B를 주입하고 B가 A를 주입할 경우 (직접 순환 참조)
  • A가 B를 주입하고 B가 C를 주입하며 C가 A를 주입할 때 (간접 순환 참조)

순환 참조가 생길 경우 다음과 같은 문제가 발생합니다.

Circular dependency 문제

  • Initialization deadlock: 애플리케이션이 실행하면서 Bean들을 초기화 하는 과정 중 deadlock이 발생해 실행에 실패할 수 있습니다.
  • Memory leaks: 애플리케이션이 실행되더라도 실행간 서로간의 참조로 인해 메모리 해제가 되지 않아 GC에서 자원을 해제하지 못해 메모리 누수가 발생할 수 있습니다.
  • Code maintainability: 순환 종속성으로 인해 클래스의 책임을 격리하기가 어려워져 코드를 테스트하고 유지보수하기가 어려워집니다.

Spring Boot Circular dependency Detection

Spring Boot에서는 Circular dependency가 발생할 경우 애플리케이션 실행간 다음과 같은 로그가 발생합니다.

Error creating bean with name 'A': Unsatisfied dependency expressed through field 'B'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'B': Requested bean is currently in creation: Is there an unresolvable circular reference?

Circular dependency가 어떤 Bean에서 발생했는지에 대해 자세하게 로그로 남겨주어 해당 부분을 해결해야만 정상적인 실행이 가능하도록 하였습니다.

그렇다면 어떻게 해결해야 할까요?

How to Solve Circular dependency

1. 애플리케이션 아키텍쳐를 다시 디자인

애플리케이션에서 순환참조가 발생할 수 없는 아키텍쳐로 재디자인 합니다. 대표적인 예시로는 Hexagonal Architecute가 있습니다.

2. @Lazy annotation 사용

Spring Boot는 애플리케이션 초기화간 필요한 의존성에 대해서 바로 구성하는 특징이 있습니다. @Lazy를 이용할 경우, Bean 초기화간 해당 Bean이 실제로 필요로 할 때까지 Bean 초기화를 지연시킬 수 있습니다.

@Component
public class A {
    @Autowired
    @Lazy
    private B b;
}

3. setter or method-based injection 사용(비권장)

setter injection 또는 @Autowired를 통한 의존성 주입 방식은 관련된 Bean을 인스턴스화 한 뒤 초기화 할 수 있다는 특징이 있습니다.

다만, Spring Boot에서는 injection 방식을 Constructor Injection을 권장한다는 글을 공식 문서를 통해 제공하고 있어 해당 방식은 비권장드립니다. (참고)

@Component
public class A {
    private B b;

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

4. @PostConstruct annotation 사용

Spring boot 에서는 @PostConstruct를 통해 모든 Bean이 생성된 이후 추가 초기화를 할 수 있습니다. 해당 방식을 통해 모든 Bean이 생성될 때까지 DI 작업을 연기하여 Circular dependency를 해결할 수 있습니다.

@Component 
공개  클래스  A { 
    @Autowired 
    비공개 B b; 

    @PostConstruct 
    public  void  init ( ) { 
        // 여기서 'b'에 의존하는 작업을 수행합니다. 
    } 
}

결론

Spring Boot의 Circular dependency는 종종 발생하는 문제입니다. Circular dependency가 왜 발생하는지 원인을 알고 해결방법을 알고 있다면 이는 개발자에게 더이상 어려운 문제가 아니라 생각합니다.

다만, 2~4번과 같은 방식은 빠르게 해결할 수 있는 방법들이지만 해당 코드로 지속적인 개발을 진행한다면 계속해서 Circular dependency가 발생할 확률이 높은 문제가 있습니다.

따라서 원인을 해결하기 이전에 왜 Circular dependency가 발생했을지에 대한 고민을 깊게 해보고 아키텍쳐가 원인인지 개발자의 클래스 분리가 문제인지 확인해보신 뒤 해결을 진행하시는 것을 권장드립니다.

읽어주셔서 감사합니다.

Reference

https://muradiodev.medium.com/top-difficult-java-spring-boot-problems-every-developer-should-know-unraveling-the-circular-c495517ea84a

https://www.baeldung.com/circular-dependencies-in-spring

profile
Gelog 나쁜 것만 드려요~

0개의 댓글