1. 빈이 중복 조회되는 상황
현재 실습하고 있는 예제에서 상품을 주문했을 때 할인 정책을 DiscountPolicy라는 interface를 구현해서 사용하고있다. 이럴 때 DiscountPolicy 인터페이스 구현체는 고정 할인 fixDiscount와 할인률을 정해서 할인해주는 rateDiscount두개가 있다.
orderServiceImpl에서 DIP를 지키기 위해 생성자 주입을 통해 인터페이스 타입 파라미터로 의존성을 주입받으려 할 때 현재 빈에는 fixdiscount와 rateDiscount 두개의 DisountPolicy 타입 빈이 등록돼있다.
스프링이 의존성 주입을 할 때 주입 대상을 타입 기준으로 조회를 하기 때문에 현재 상황에서는 DiscountPolicy 구현체 두개가 조회되어 expected single matching bean but found 2오류가 발생한다.
orderServiceImpl.java
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = rateDiscountPolicy;
}
FixDiscountPolicy
@Component
public class FixDiscountPolicy implements DiscountPolicy {
}
RateDiscountPolicy
@Component
public class RateDiscountPolicy implements DiscountPolicy{
}
2. 해결 방법
1. 필드명 or 생성자의 파라미터명으로 맞춰주기
스프링은 타입으로 매칭을 시도하고 만약 중복되는 빈이 여러개 있으면 필드명, 또는 파라미터명을 대조해서 우선순위를 두기 때문에 가능한 방법이다.
@Autowired
//public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) {
this.memberRepository = memberRepository;
//this.discountPolicy = discountPolicy;
this.discountPolicy = rateDiscountPolicy;
}
2. @Qualifier 어노테이션 사용
빈 조회 우선순위를 정해주는 @Qualifier로 클래스의 별명을 지어주고 의존성을 주입 받을 곳에서 @Qualifier로 내가 먼저 조회할 빈의 별명을 설정하면, 설정한 별명을 가진 클래스를 먼저 찾고, 만약 못찾는다면 같은 별명의 실제 빈으로 등록된 이름을 찾는다. 이래도 못찾으면 NoSuchBeanDefinitionException 예외를 뱉는다(실제로 해보니까 Qualifier에 설정한 이름이 다르면 오류를 뱉어버린다)
orderServiceImpl.java
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
RateDiscountPolicy
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy{
}
FixDiscountPolicy
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {
}
3. @Primary 어노테이션 사용
@Primary 어노테이션이 붙은 빈을 먼저 가져온다.
orderServiceImpl.java
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
RateDiscountPolicy
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy{
}
FixDiscountPolicy
@Component
public class FixDiscountPolicy implements DiscountPolicy {
}
4. @Qualifier와 @Primary가 동시에 존재할때는?
@Qualifier는 어노테이션이 붙은 빈을 먼저 그냥 가져와버리기 때문에 기본값처럼 동작한다. 하지만 @Primary는 개발자가 지정한 이름을 통해 지정해서 가져오기 때문에 조금더 정확하게 동작한다. 그렇기 때문에 스프링에서는 범위가 좁고 정확한 @Qualifier 어노테이션이 붙은 빈을 우선적으로 가져온다.
3. 정리
스프링이 빈을 조회하는 순서는 다음과 같다.
1. 타입으로 빈을 조회한다. 1개의 단일 빈이 조회되면 바로 사용하고 2개 이상의 동일한 타입을 가진 빈이 조회되면 아래의 과정을 거친다.
2. 주입받는 곳에 등록된 @Qualifier의 별명과 일치하는 Qualifier를 먼저 찾는다. 못찾으면 실제로 등록된 빈 중 별명과 동일한 이름의 빈을 가져온다.
3. @Primary로 등록된 빈을 가져온다.
4. 조회를 못한경우 파라미너, 필드명과 동일한 이름을 가진 빈을 가져온다
5. 없으면 Exception
'Backend > Spring' 카테고리의 다른 글
[Spring/Junit5] @Test(expected = ~) Junit5에서 사용하기 (0) | 2024.01.26 |
---|---|
[Spring] 스프링 빈 생명주기 (0) | 2024.01.10 |
[Spring/Error][해결] 스프링 리액트 통합 빌드 - Unable to access jarfile (0) | 2023.12.25 |
[Spring/Error][해결] Spring-React 통합 빌드 오류 - An input file was expected to be present but it doesn't exist. (0) | 2023.12.25 |
[Spring/Error][해결] VSCode jdk 버전 업그레이드하기 (0) | 2023.12.25 |