본문 바로가기

[Spring] 한 컨테이너 안에 같은 타입의 빈이 2개라면?

@xuv22025. 6. 19. 22:03

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

스프링 컨테이너 안에 한타입의 하나의 빈만 존재한다면 아무 걱정이 없겠지만, 만약 FixDiscountPolicy() 나 RateDiscountPolicy() 처럼 한 타입의 두개의 빈이 존재하면 DI에 어떤 영향을 줄까?

 

Bean 이 Unique 하지 않아요

DI 프레임워크가 자동 의존 관계 주입을 시도하는 과정에서 동일 타입 빈 2개를 만나면 다음과 같이 NoUniqueBeanDefinitionException 예외가 발생한다.

NoUniqueBeanDefinitionException: No qualifying bean of type
'hello.core.discount.DiscountPolicy' available: expected single matching bean
but found 2: fixDiscountPolicy,rateDiscountPolicy

오류를 읽어보면 "빈 타입이 하나만 매칭 되길 기대했지만, 2개를 발견했다" 라는 것을 알수 있다.

이때, "그러면 그냥 각각 하위 타입으로 주입 받으면 안되나요?" 라고 생각할 수도 있는데, 이는 명백히 DIP를 위해하고, 애플리케이션의 유연성이 떨어진다.

Bean을 등록할 때 @Qualifer 에노테이션으로 이름을 다르게 수동으로 등록하면 되지 않나요? 라는 질문의 답은 "맞다." 이지만, 의존 관계 자동 주입에서도 여러 방법이 존재한다.

 

같은 타입의 빈 문제 해결 방안

@Autowired 필드 명 매칭

@Autowired는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름으로 추가 매칭한다.

예를 들어 이전 예시처럼 FixDiscountPolicy() 와 RateDiscountPolicy() 구현체가 존재할 때, 우리는 다음과 같이 의존 관계를 주입 받았었다.

@Autowired
private DiscountPolicy discountPolicy

위와 같은 코드에서는 discountPolicy라는 이름의 구현체는 없기 때문에 NoUniqueBean 예외가 발생한다.

 

하지만 다음처럼 코드를 변경해보자

@Autowired
private DiscountPolicy rateDiscountPolicy

필드명이 빈의 이름과 같다면 RateDiscountPolicy() 가 정상적으로 주입된다.

즉, @Autowired는 먼저 타입 매칭을 시도하고, 여러 빈이 있을 때 추가로 기능이 동작하도록 해놓았는데 이때 필드 명과 파라미터 명으로 빈 이름을 매칭한다.

 

@Qualifier 사용

@Qualifier는 추가 구분자를 붙여주는 방법이다.

중요한 점은 이것은 추가 구분자일 뿐이지, 빈 이름을 변경하는 것이 아니다 !

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

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

주입시 @Qualifier를 붙이고 등록한 이름을 적는다.

사용 예시는 다음과 같다

// 생성자 자동 주입 예시
@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;
}

 

@Primary 사용

@Primary는 우선 순위를 정하는 방법이다. -> 자주 사용하자 !

@Autowired를 통해 DI 시 여러 빈들이 조회 되면 @Primary를 가진 빈이 우선권을 가진다.

@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy{}

@Component
public class FixDiscountPolicy implements DiscountPolicy{}

 

이렇게 하면 DiscountPolicy를 주입 받아야하는 생성자는 수정자는 RateDiscountPolicy를 우선으로 주입한다.


@Qualifier 와 @Primary 중 어떤걸 쓸까?

@Qualifier는 코드 작성량이 많다. 기왕이면 @Primary를 쓰자.

하지만 이 둘도 적절한 조화가 필요한데, 메인 데이터베이스의 커넥션을 가져오는 빈은 @Primary로 등록하고, 가끔 사용하는 서브 데이터베이스의 커넥션은 @Qualifier 를 사용하자


우선순위

스프링은 언제나 자세한 것이 우선순위가 높다. @Primary는 디폴트 같은 느낌이므로 기본값으로 동작하는 것이다.

즉, 수동 + 상세하게 등록한 @Qualifier가 우선권이 높다.

xuv2
@xuv2 :: xuvlog

폭싹 늙었수다

목차