[Spring] 조회한 빈이 2개 이상일 때

mj·2024년 4월 13일

Spring 스프링

목록 보기
4/5

스프링 핵심원리 기본편 - 섹션7. 의존관계 자동주입 - 조회할 빈이 2개 이상일 때 - 문제

@Autowired는 타입으로 조회한다.

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

이전에는 FixDiscountPolicy에만 @Component를 붙여 빈으로 등록시켰지만 테스트를 위해 RateDiscountPolicy도 @Component를 붙여 빈으로 등록시킴.

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

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

FixDiscountPolicy, RateDiscountPolicy 모두 스프링 빈에 등록됐을 때 의존관계 자동주입을 실행하면 NoUniqueBeanDefinitionException 오류 발생!

@Autowired는 타입으로 조회하는데 DiscountPolicy타입이 2개발견됨 -> FixDiscountPolicy, RateDiscountPolicy

하위타입으로 지정할 수도 있지만, 이렇게하면 DIP위반하게되고 유연성 떨어짐.
이럴땐 어떻게 해야할까? 해결방법3가지가 있다.


해결방법 3가지

1. @Autowired 필드 명 매칭
2. @Qualifier -> @Qualifier끼리 매칭 -> 빈 이름 매칭
3. @Primary 사용



1. @Autowired 필드 명 매칭

  • @Autowired 는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.
  • (필드 명 매칭은 먼저 타입 매칭을 시도 하고 그 결과에 여러 빈이 있을 때 추가로 동작하는 기능이다.)

1️⃣ 타입매칭
2️⃣ 결과가2개 이상일 때 필드명, 파라미터 명으로 빈 이름 매칭


필드 명을 빈 이름으로 변경
discountPolicy -> rateDiscountPolicy

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy rateDiscountPolicy;

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


2. @Qualifier 사용

  • 추가 구분자를 붙여주는 방법

1️⃣ @Qualifier끼리 매칭
2️⃣ 빈 이름 매칭
3️⃣ NoSuchBeanDefinitionException 예외 발생


빈 등록시 @Qualifier를 붙여 준다.

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

생성자 자동 주입 예시 )

@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;
    }

수정자 자동 주입 예시 )

 @Autowired
 public DiscountPolicy setDiscountPolicy(@Qualifier("mainDiscountPolicy")
 DiscountPolicy discountPolicy) {
     this.discountPolicy = discountPolicy;
 }
  • @Qualifier 로 주입할 때 @Qualifier("mainDiscountPolicy") 를 못찾으면 어떻게 될까?
    ➡️ 그러면 mainDiscountPolicy라는 이름의 스프링 빈을 추가로 찾는다. 하지만 경험상 @Qualifier@Qualifier 를 찾 는 용도로만 사용하는게 명확하고 좋다.


3. @Primary 사용

//RateDiscountPolicy가 우선권을 갖음 
@Component
 @Primary
 public class RateDiscountPolicy implements DiscountPolicy {}


 @Component
 public class FixDiscountPolicy implements DiscountPolicy {}
//생성자@Autowiredpublic OrderServiceImpl(MemberRepository memberRepository, 
                         DiscountPolicy discountPolicy) {
     this.memberRepository = memberRepository;
     this.discountPolicy = discountPolicy;
 }

//수정자@Autowiredpublic DiscountPolicy setDiscountPolicy(DiscountPolicy discountPolicy) { 
     this.discountPolicy = discountPolicy;
 }
  • @Primary와 @Quailifer 둘 중 어떤거 사용하지?
    @Qualifier의 단점 : 모든 코드에 @Qualifier를 붙여줘야 함. 코드 길어짐. 귀찮음.

  • @Primary와 @Quailifer의 우선순위
    @Quailifer 우선 > @Primary
    스프링은 넓은 범위의 선택권 보다는 좁은 범위의 선택권이 우선순위가 높다.


profile
일단 하자.

0개의 댓글