@Qualifier, @Primary를 사용하여 스프링 빈 조회

2023. 5. 6. 19:11JAVA/Spring

1. @Qualifier, @Primary의 필요성

@Autowired나 스프링 컨테이너의 getBean()과 같은 메소드를 이용하여 스프링 컨테이너의 스프링 빈을 타입으로 조회하는 경우가 있습니다. 그런데 스프링 컨테이너에 동일한 타입의 스프링 빈이 2개 이상 있는 경우 타입으로 조회시 NoUniqueBeanDefinitionException 에러가 발생할 수 있습니다.

 

예를 들어 DiscountPolicy라는 인터페이스의 구현체가 2개가 있고 두 구현체 모두 스프링 컨테이너에 스프링 빈으로 등록되어 있는 상태입니다.

위와 같은 상황에서 @Autowired를 사용하여 자동 주입을 수행할때 스프링 빈의 타입으로 조회합니다.

 

 

위 그림과 같이 자동 주입시 DiscountPolicy 인터페이스 타입으로 등록되어 있는 스프링 빈을 조회하게 됩니다. 그러나 동일한 타입이 2개(FixDiscountPolicy, RateDiscountPolicy)여서 NoUniqueBeanDefinitionException 에러가 발생합니다.

 

위와 같이 동일한 스프링 빈 타입이 2개 이상인 경우 어떤 빈을 선택해야 하는지 설정해야 하는 필요성이 있습니다. 이러한 문제를 해결하기 위한 2가지 방법이 다음과 같습니다.

  • @Qualifier를 이용한 특정한 스프링 빈 선택
  • @Primary를 이용한 제일 우선적인 스프링 빈 선택

 

2. @Qualifier

@Qualifier 애노테이션은 동일한 타입의 스프링 빈이 2개 이상이 경우 특정한 스프링빈을 선택할 수 있는 기능을 수행합니다. 예를 들어 위 문제점에서 FixDiscountPolicy와 RateDiscountPolicy 스프링 빈은 모두 DiscountPolicy라는 인터페이스의 타입을 가지고 있습니다. 이렇게 되면 의존관계 주입시 스프링 컨테이너가 어떤 스프링 빈을 주입할지 결정할 수 없습니다. 그래서 @Qualifier를 이용하여 의존관계 주입시 특정한 스프링 빈을 선택하여 주입할 수 있도록 합니다.

 

@Qualifier는 두군데에 설정하여야 합니다.

  • 스프링 빈으로 등록할 클래스
  • 의존관계 참조 객체

 

예를 들어 OrderServiceImpl 클래스는 DiscountPolicy 객체에게 의존하고 있습니다. 그러면 다음과 같이 설정합니다.

@Component
@Qualifier("rateDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {
	...
}

 

의존관계 참조 객체에 생성자 주입 방법을 통하여 주입받는다고 하면 다음과 같이 설정할 수 있습니다.

 

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, 
        @Qualifier("rateDiscountPolicy") DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
    ...
}

 

3. @Primary

@Primary는 동일한 타입의 스프링 빈 중에서 우선순위를 가지는 방법입니다. 예를 들어 동일한 타입의 스프링 빈이 2개 이상 있으면 @Primary 애노테이션을 가지는 스프링 빈이 우선적으로 주입됩니다.

 

예를 들어 RateDiscountPolicy 스프링 빈을 우선적으로 주입하고 싶다면 다음과 같이 설정합니다.

 

@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {
	// ...
}

 

4. @Qualifier와 @Primary간의 우선순위

@Primary는 기본값 처럼 동작하고 @Qualifier는 매우 상세하게 동작합니다. 이러한 경우 스프링은 자동보다는 수동이, 넓은 범위의 선택권보다는 좁은 범위의 선택권이 우선순위가 높습니다. 따라서 @Qualifier가 우선권이 더 높습니다.

 

References

스프링 핵심 원리 - 기본편