Notice
«   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
01-25 05:03
Today
Total
관리 메뉴

그날그날 공부기록

스프링 빈 중복 시 오류 & 해결방법 본문

Spring 공부

스프링 빈 중복 시 오류 & 해결방법

given_dragon 2022. 8. 2. 18:18

@Autowired를 사용하여 빈을 조회하면 타입이 기준이 된다.

의존관계를 주입받을 경우 DIP를 위해 구현 객체가 아닌 인터페이스를 사용한다.

하지만 2개 이상 빈의 타입이 같으면 오류가 발생한다.

 

다음과 같이 할인을 위한 DiscountPolicy 인터페이스와 구현 객체가 2개 있다고 생각해보자.

DiscountPolocy를 상속받아 %로 할인을 적용하는 구현체
DiscountPolocy를 상속받아 금액으로 할인을 적용하는 구현체

두 객체 모두 @Component를 작성해 스프링 빈으로 스캔되고 등록된다.

이 상황에서 어떤 서비스에서 상위 타입인 DiscountPolicy로 의존관계를 주입받게 된다면 같은 타입의 빈이 2개가 되기 때문에 NoUniqueBeanDefinitionException오류가 발생한다.

물론 구체적인 타입인 RateDiscountPolicy로 의존관계를 주입받는다면 에러는 발생하지 않겠지만 DIP를 위반하게 된다.


해결방법

1. @Autowired를 사용했다면 필드 명을 빈 이름으로 변경하면 된다.

→ 중복된 타입의 빈이 검색됐을 경우 이름이 같은 필드가 있다면 해당 빈이 주입된다.

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

다음과 같이 DiscountPolicy를 주입받는다면 에러가 난다.

하지만 필드 이름을 사용하려는 빈 이름인 rateDiscountPolicy 혹은 fixDiscountPolicy로 변경한다면 그 이름에 맞는 빈이 주입된다!

 

 

2. @Qualifier 사용하여 스프링이 빈을 매칭 할 수 있는 방법을 제공해준다.

빈으로 등록할 객체와 의존관계를 주입받는 필드에 @Qualifier(”빈 이름")을 적어주면 해당 빈 이름에 맞는 빈을 주입해준다.

//빈 등록 객체
@Component
@Qualifier("rate")
public class RateDiscountPolicy implements DiscountPolicy{
    ...
}
//DiscountPolicy를 사용하는 서비스
@Component
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

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

이렇게 빈과 필드를 @Qualifier로 매칭해주면 된다.

빈 이름을 설정한 문자열로 바꾸는 것이 아닌 주입할 빈을 찾을 수 있는 추가적인 수단을 주는것이다.

만약 @Qualifier로 매칭 되는 빈이 없다면, 그냥 필드명과 동일한 빈을 검색한다.

 

순서는 다음과 같다. @Qualifier로 매칭 → 필드 이름 → 에러

 

 

3. @Primary를 사용한다.

빈으로 등록할 객체에 해당 애노테이션을 적어주면 빈 타입 중복 시 @Primary객체를 우선 주입한다.

다음과 같이 중복되는 타입의 빈이 2개일 경우 @Primary가 붙은 RateDiscountPolicy가 선택되어 주입된다.

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

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

+ 자동 생성된 생성자에 Qualifier추가하기

@RequiredArgsConstructor을 사용할 경우 @Qualifier 가 적용되지 않는다.

이유는 @RequiredArgsConstructor는 애노테이션까지 포함시켜 생성자를 만들지 않기 때문이다.

하지만 포함시킬 수 있는 방법이 있다!

 

  1. src/main/java에 lombok.config 파일을 생성한다. → src/main/java/lombok.config
  2. lombok.config 파일에 다음 코드를 추가한다.
    lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
  3. 프로젝트를 다시 컴파일 한다.
    Intellij를 사용한다면 out이라는 폴더를 삭제하고 해야한다.
    gradle은 gradlew clean을 하고 실행한다.
@Component
@Qualifier("rate")
public class RateDiscountPolicy implements DiscountPolicy{
    ...
}
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;

    @Qualifier("rate")
    private final DiscountPolicy discountPolicy;
    ...
}

 

설정파일을 따로 적용했으므로 생성자에 @Qualifier가 추가되어 생성된다.

 

.class를 확인해보면 애노테이션이 추가되어 생성자가 만들어졌다.

 


출처

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

 

Comments