Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

조회할 빈이 2개 이상일 경우? 본문

인프런/[인프런] Spring 핵심원리 이해

조회할 빈이 2개 이상일 경우?

Seung__ 2021. 7. 30. 14:47
  • @Autowired는 타입으로 조회를 한다.
  • 근데 스프링 컨테이너에 같은 타입의 빈이 여러 개 있으면..?

 

 

1. 같은 타입이 2개!


  • DiscountPolicy의 구현체는 2개가 있다.
  • RateDiscountPolicy
    @Component
    public class RateDiscountPolicy implements DiscountPolicy{​
  • FixDiscountPolicy
    @Component
    public class FixDiscountPolicy implements DiscountPolicy{​
  • 둘 다 @Component등록이 되면 어떻게 될까?

 

 

2. Test


public class AutoAppConfigTest {

    @Test
    void basicScan(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);

        MemberService memberService = ac.getBean(MemberService.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
    }
}​

 

  • NoUniqueBeanDefinitionException:  예외가 발생한다.
NoUniqueBeanDefinitionException : No qualifying bean of type 'hello.core.discount.DiscountPolicy' available: 
expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy
  • OrderServiceImpl
    @Component
    @RequiredArgsConstructor //final 붙은 필드를 받는 생성자를 만듦
    public class OrderServiceImpl implements OrderService{
    
        private  final MemberRepository memberRepository;
        
        private  final DiscountPolicy discountPolicy;​
    OrderServiceImple은 DiscountPolicy를 컨테이너에서 주입을 받는다.
    하지만 DiscountPolicy타입의 빈은 FixDiscountPolicy, RateDiscountPlicy 두 개 가 존재하여 뭐를 가지고 올지..모름.
  • 어떻게 해야할까...?
  • 구체적인 하위 타입의 빈을 지정할 수도 있다. 하지만 하위 타입으로 지정하는 것은 DIP 위반이다! 멈춰!
  • 그리고 이름만 다르고 또오오옥같은 스프링 빈이 있을 경우 해결할 수 없음..
  • 스프링 빈을 수동으로 등록하여 우선순위를 활용하는 방법도 있겠지만..흠..

 

 

3. 자동 관계주입에서 중복 문제 해결


세 가지 방법이 있다.

  • @Autowired 필드명 매칭
  • @Qualifier
  • @Primary

 

 

4. @Autowired 필드명 매칭


  • @Autowired는 타입 매칭을 시도하고, 여러 빈이 있으면 필드 이름(파라미터 이름)으로 빈 이름을 추가 매칭함!
  • 생성자 같은 경우 파라미터 이름을 봄.
  • 기존 코드
    @Component
    @RequiredArgsConstructor //final 붙은 필드를 받는 생성자를 만듦
    public class OrderServiceImpl implements OrderService{
    
        private  final MemberRepository memberRepository;
    
        private  final DiscountPolicy discountPolicy; //이렇게하면 인터페이스에만 의존함.​
     

DiscountPolicy의 필드명이 discountPolicy로 되어있다. 필드 명을 받고자 하는 구현체의 bean name으로 변경하면

해당 빈을 주입받을 수 있다.

  • 변경 코드
    @Component
    @RequiredArgsConstructor //final 붙은 필드를 받는 생성자를 만듦
    public class OrderServiceImpl implements OrderService{
    
        private  final MemberRepository memberRepository;
    
        private  final DiscountPolicy rateDiscountPolicy;​
  • Test
    public class AutoAppConfigTest {
    
        @Test
        void basicScan(){
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
    
            MemberService memberService = ac.getBean(MemberService.class);
            Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
        }
    }​
  • 즉 @Autowired로 연결할 타입과 동일한 빈을 자식까지 포함해서 다 끌고옴.
  • 어? 같은 타입이 여러개가 있네?
  • 필드 이름이뭐임?, 파라미터 명이 뭐임? 
  • 필드 이름이랑 파라미터 이름이 빈 이름 같은애로 가져올꼐! 메다닥

 

 

 

3. @Qualifier 


  • 추가 구분자를 붙여주는 방법임.
  • 주입 시 추가적인 방법을 제공하지만, 빈 이름을 변경하는 것은 아님!
  • @Component
    @Qualifier("mainDiscountPolicy")
    public class RateDiscountPolicy implements DiscountPolicy{
  • 그리고 빈을 주입 받을 곳 에서 @Qualifier를 동일하게 붙여주면 된다.
  • 그러면 같은 타입 중 "mainDiscountPolicy" 가 붙은 빈을 가지고 온다.
  • 어? 근데 lombok이랑 같이 사용을 못해!
  • RequiredArgsConstructor를 빼고 생성자를 별도로 만들어서 적용하면 된다.
  • @Component
    //@RequiredArgsConstructor //final 붙은 필드를 받는 생성자를 만듦
    public class OrderServiceImpl implements OrderService{
    
        private final MemberRepository memberRepository;
    
        
        private final DiscountPolicy discountPolicy; //이렇게하면 인터페이스에만 의존함.
    //private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
    
        @Autowired
        public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
            this.memberRepository = memberRepository;
            this.discountPolicy = discountPolicy;
        }
  • 흠 그러면 만약 지정한 이름의 @Qualifier를 못찾으면..?
  • mainDiscoutPolicy라는 이름의 스프링 빈을 추가로 찾음..
  • 그러니 @Qualifier는 @Qualifier만 찾는 용도로만 사용하자.
  • 직접 등록시에도 사용 가능.

 

 

 

4. @Primary


  • 우선순위를 정할 수 있다.
  • @Autowired시 여러 빈이 매칭되면 @Pairmary가 1등
  • RateDiscountPolicy에 @Primay
  • @Component
    @Primary
    public class RateDiscountPolicy implements DiscountPolicy{

 

5. @Qualifier vs @Primary


  • @Qualifier의 단점은 주입받을 때 모든 코드에 @Qualifer붙여줘야 함.
  • @Primary는 그럴 필요는 없음
  • 예를들어 main으로 사용하는 어떤 클래스? 구현체가 있다고 하면 main으로 사용하는 구현체는 
    @Primary로 등록해놓고, 가끔 서브로 사용하는 구현체는 @Qualifer를 등록
    따라서 평상시에는 그냥 쓰다가 sub를 호출하는 메서드에만 @Qualifier를 통해 명시적으로 사용.
  • 우선순위 : 자세한 것이 우선순위가 높다
    - @Primary는 기본 값 처럼 작동, .
    - @Qualifier는 이름까지 지정, 우선순위가 더 높음

 

6. GitHub : 210730 @Qualifier, @Primary


 

 

GitHub - bsh6463/SpringCoreFunction

Contribute to bsh6463/SpringCoreFunction development by creating an account on GitHub.

github.com

 

Comments