스프링 컨테이너는 조회 대상의 타입이 2개 이상일 경우 에러를 발생 시킨다.
이 경우 하위 타입으로 지정해도 되지만 DIP, OCP 위반이다.
@Autowired 필드 명 or 파라미터 명으로 빈 이름 매칭
아래와 같은 코드가 있을 경우 같은 타입의 빈이 두개이므로 등록을 할 수 없는 에러가 발생한다.
DiscountPolicy타입으로 fixDiscountPolicy,rateDiscountPolicy 2개의 하위 타입이 있기 때문에 나는 에러
이다.(NoUniqueBeanDefinitionException
)
의존 주입을 시도할 때 발생하는 것이다.
@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;
}
}
public class FixDiscountPolicy implements DiscountPolicy {...}
public class RateDiscountPolicy implements DiscountPolicy {...}
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:\
No qualifying bean of type 'hello.core.discount.DiscountPolicy' available: expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy
생명 주기
@Autowired 파라미터나 필드명을 fixDiscountPolicy와 같이 변경하면 된다.
- 타입 매칭 시도 후 중복이 된다면?
- 이름 매칭을 시도하게 된다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy fixDiscountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = fixDiscountPolicy;
}
- 필드, 수정자 주입에도 동일하다.
@Qualifier
주입 시 추가로 구분자만 할당 해주는 방법이다. 빈 이름을 변경하는 것은 아니다.
그냥 별명일 뿐이다.
아래와 같이 빈에 별명을 붙여준다.
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
주입 시 원하는 @Qualifier를 선택한다.
그럼 RateDiscountPolicy가 등록되게 된다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- 필드, 수정자 주입에도 동일하다.
생명 주기
- @Qualifier끼리 매칭
- 빈 이름으로 매칭
NoSuchBeanDefinitionException
@Primary
우선 순위를 정하는 방법이다.
rateDiscounPolicy가 우선권을 가지도록 하는 코드이다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
사용 코드는 아무것도 건들 것이 없다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Qualifier와 @Primary중 뭘 사용하지?
@Qualifier를 주입 받을 때 모든 코드에 @Qualifier 달아줘야 하므로 불편하고, 코드가 지저분해 질 수 있다.
하지만 @Primary는 그럴 필요가 없다.
이렇게 결정하자.
90% 비율로 메인 DB가 사용되고, 10%비율로 서브 DB가 사용된다고 하자.
이때 메인에는 @Primary를 사용하고, 가끔 사용되는 서브 DB에는 명시적으로 @Qualifier를 지정해서
코드를 깔끔하게 유지할 수 있다. 즉, 필요할 경우만 명시적으로 @Qualifier를 사용하는 것이다.
우선 순위
스프링은 자동 보다는 수동이, 넓은 범위 보다는 좁은 범위의 선택이 우선 시 된다.
즉, @Primary 보다 @Qualifier가 더 상세하기 때문에 이와 같이 사용할 수 있다.
@Qualifier 애노테이션의로 만들기
@Qualifier를 사용하게 되면 문자열이기 때문에 런타임 에러를 잠재하게 된다.
그래서 컴파일 에러로 잡기 위해 애노테이션을 활용할 수 있다.
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
@MainDiscountPolicy
@Component
public class RateDiscountPolicy implements DiscountPolicy {...}
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
'Spring' 카테고리의 다른 글
스프링 빈의 생명주기 (0) | 2022.01.14 |
---|---|
스프링에서 모든 타입의 빈을 동적으로 관리하기 (0) | 2022.01.14 |
스프링의 @ComponentScan (0) | 2022.01.14 |
스프링의 @Configuration과 싱글톤의 관계 (0) | 2022.01.14 |
스프링의 BeanDefinition (0) | 2022.01.14 |