본문 바로가기
[inflearn] 스프링 핵심 원리 - 기본편/섹션 7 - 의존관계 자동 주입

7-5. 중복 빈 처리 방법

by 슬픈 야옹이 2024. 5. 26.

 

@Autowired는 주입할 빈을 타입으로 찾는다.

 

예를 들어 코드가 다음과 같을 때, 스프링 컨테이너는 DiscountPolicy 타입의 빈을 찾아 주입하는 방식이다.

@Autowired
private DiscountPolicy discountPolicy

 

등록된 빈 중에서 타입이 DiscountPolicy인 빈이 하나밖에 없다면 문제되지 않겠지만,

아래처럼 같은 타입의 빈이 여러개 등록되어 있다면 오류 (NoUniqueBeanDefinitionException)가 발생한다.

@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}

 

 

 

 

 

이러한 중복을 해결하는 방법이 몇 가지 있다.

  • @Autowired에 필드명 지정
  • @Qualifier("alterName")
  • @Primary

 

@Autowired에 필드명 매칭

@Autowired는

1. 처음에 타입으로 매칭하고,

2. 중복되는 빈이 있을 경우 필드 이름, 파라미터 이름으로 빈 이름을 다시 매칭한다.

 

즉 필드명을 다음과 같이 바꾸면 빈 중복 문제를 해결할 수 있다.

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;

    private final DiscountPolicy rateDiscountPolicy; // 필드 이름을 rateDiscountPolicy로 변경

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

}

 

 

 

@Qualifier("alterName")

@Qualifier는 추가적인 구분자를 지정해주는 방식이다.

 

클래스 선언부에 @Qualifier("alterName")을 붙여주면, 해당 클래스는 기본적으로 지정되는 빈 이름에 추가적으로 alterName이라는 이름으로 조회할 수 있다.

 

방법

1. 빈으로 등록할 클래스 선언부에 @Qualifier를 붙인다.

@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {

    ...

}

 

다른 빈에도 붙여줄지는 임의로 판단

@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {

   ...

}

 

 

2. 해당 빈을 주입받는 코드에 @Qualifier로 등록한 구분자를 지정한다.

 

생성자 자동 주입 예시 (파라미터에 지정, 수정자도 동일)

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;

    private final DiscountPolicy discountPolicy;

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

}

 

 

빈 수동 등록 예시

@Bean
@Qualifier("mainDiscountPolicy")
public DiscountPolicy discountPolicy() {
    return new ...
}

 

 

특징

  • @Qualifier로 추가한 구분자와 빈 이름은 혼용해서 사용 가능하다.
    • 다만, @Qualifier로 추가한 구분자는 추가한 구분자끼리만 구분하는 용도로 쓰는 것이 바람직하다.
      상식적으로 생각해봐도 혼용하면 헷갈린다.
  • 구분자 지정에 단순 문자열을 사용하기 때문에, 오타 등을 잡아주지 못한다.
  • 필요할 때마다 @Qualifier를 붙여주어야 한다. (해당 빈을 주입받는 모든 코드에 @Qualifier를 붙여주어야 한다.)

 

 

 

 

 

@Primary

@Primary는 빈에 우선 순위를 부여하는 방식이다.

 

같은 타입의 빈이 여러개 매칭될 경우 @Primary로 지정된 빈이 우선권을 가진다.

 

예를 들어 RateDiscountPolicy가 우선적으로 매칭되도록 하고 싶다면, 해당 클래스에 @Primary를 붙여주면 된다.

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

 

@Component
public class FixDiscountPolicy implements DiscountPolicy {
  ...
}

 

 

 

권장 사용 방식

주로 사용하는 빈을 @Primary로 등록하고, 특별한 경우에 사용할 빈을 @Qualifier로 지정해서 사용하도록 설정하면 깔끔하게 코드를 작성할 수 있다.

 

즉 @Priamry와 @Qualifier를 함께 사용하는 것이 권장된다.

 

 

@Primary와 @Qualifier 사이의 우선 순위

스프링은 넓은 범위보다는 자세한 범위에 대한 우선순위가 더 높다.

즉 @Primary로 등록한 빈이 있더라도, 주입할 빈을 @Qualifier로 명시한 경우 @Qualifier로 지정한 빈으로 적용된다.