[Spring] NoUniqueBeanDefinitionException 예외 해결방법

728x90

이 해결법을 모르는 것은 아니지만 이 현상을 자주 맞이하는게 아니다보니, 늘 쓰는방식만쓰고 지나갔던 기억이 있다.
이번 기회에 정리해두고 똑같은 상황을 맞이했을 때 조금 더 현명(?)하게 대처할 수 있도록 기록을 남긴다.

총 3가지 예외 해결방법이 있다.

  1. @Autowired 필드명 매칭
  2. @Qualifier -> @Qualifier끼리 매칭 (Bean 이름 매칭)
  3. @Primary

1. @Autowired 필드명 매칭

Autowired는 우선적으로 타입 매칭을 시도하고 타입이 매칭되는 빈이 여러개라면 이름(파라미터 이름)으로 빈 이름을 매핑한다.
(필드명 매칭은 타입 매칭 결과가 1개뿐이라면 발생하지 않는다)

@Autowired
private FooComponent foo; // FooComponent 타입의 빈이 2개 이상이면, 빈 이름이 foo 빈을 매핑!
private final FooComponent foo;

// 주 생성자 or 롬복으로 만든 생성자 주입 방식도 동일하다
public FooClass(FooComponent foo) {
    this.foo = foo
}

2. @Qualifier

@Qualifier 라는 추가 구분자를 붙여줌으로써 빈을 구분짓는 방법이다. 
(실제로 빈 이름을 변경하는 것은 아니다)

만약 @Qualifier 주입을 할 때, value의 값에 매칭되는 Bean을 찾지 못하면 어떻게 될까? 그러면 value로 입력한 값과 일치하는 스프링 빈을 추가로 찾는다(@Qualifier는 추가적인 식별 방식이지 실제로 빈 이름을 변경하는 작업은 아니라고 언급했었다).

만약 그럼에도 일치하는 Bean을 찾지 못한다면 NoSuchBeanDefinitionException 예외가 발생한다.

하지만 @Qualifier는 @Qualifier를 찾는 용도로만 쓰는 것이 명확하고 좋다. 

3. @Primary 사용 

우선순위를 정하는 방법이다.
@Autowired 시에 여러 빈이 매칭되면 @Primary가 우선권을 가진다.
(@Primary 자체가 빈을 생성하기위한 애너테이션은 아니고, 빈 생성을 위한 애너테이션을 동일하게 붙여줘야한다)

@Component
@Primary
public class MainFooComponent implements FooComponent { } // 우선권을 가진다

@Component
public class NormalFooComponent implements FooComponent { }

우선순위

만약 @Primary와 @Qualifier가 둘 다 있다면 어느 것이 먼저 동작하는가?
@Qualifier가 우선 순위가 높다!

(영한님 왈) 자세한 것이 우선권을 가져간다! 

끝맺음

사실 같은 타입에 대해서 빈이 여러개 존재하고, 이 중 하나의 빈만 가지고와서 사용한 케이스가 생각보다 별로 없었던 것 같다. 🤔
이런 케이스에 대해서 Primary를 쓸 것인가 @Qualifier를 쓸 것인가에 대한 고민이 생길 수 있을 것 같다. 
확실하게 Main인 빈이 확실한 경우라면 Primary도 충분히 고려해볼 수 있겠지만 아니라면 @Qualifier를 써야하지 않을까?

728x90