코드 예

public interface DiscountPolicy {
        //코드
}

@Component
public class FixDiscountPolicy implements DiscountPolicy {
	//코드
 }
 
 @Component
 public class RateDiscountPolicy implements DiscountPolicy {
	//코드
 }
 
 
 @ComponentScan
 public class Config {
 	코드
 }

@ComponentScan을 사용하면 @Component 어노테이션이 붙은 클래스들을 스프링 빈으로 등록하게 됩니다.

등록하게 되면 스프링 컨테이너의 스프링 빈에 등록되는 것은 클래스들의 타입이 스프링 컨테이너에 빈으로 등록됩니다.

 

 

여기서 사용된 DI는 생성자 주입 방식입니다. (DI 종류 : 생성자 주입, 필드 주입, 수정자(setter) 주입)

 

@Component
public class OrderServiceImpl implements OrderService {
	
    
	private final DiscountPolicy discountPolicy;
    
    @Autowired
    public OrderServiceImpl(DiscountPolicy discountPolicy) {
    		this.discountPolicy = discountPolicy;
    }

생성자 주입 방식에서 매개변수로 DiscountPolicy타입이 들어가 있습니다.

그럼 아래 오류 처럼 오류가 발생할 것이다

 

*오류 내용

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl' defined in file [경로]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.discount.DiscountPolicy' available: expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy

왜냐하면 DiscountPolicy 타입인 RateDiscountPolicy와 FixDiscountPolicy가 중복으로 등록되어 있기 때문입니다.

오류 내용도 함께 확인해보면 마지막 줄에

expected single matching bean but found 2: fixDiscountPolicy, rateDiscountPolicy

라는 오류가 발생하여 어떤 오류인지는 확인하기 쉬울 것입니다.

 

빈이 중복으로 등록되었을때  해결 방법 3가지

1. @Autowired

2. @Qualifier

3. @Primary

 

1. Autowired

- 생성자 주입에서 매개변수로 있는 변수 이름을 변경해주면 됩니다.

 

@Component
public class OrderServiceImpl implements OrderService {
	
    
	private final DiscountPolicy discountPolicy;
    
    @Autowired
    public OrderServiceImpl(DiscountPolicy rateDiscountPolicy) {
    		this.discountPolicy = rateDiscountPolicy;
    }

discountPolicy -> rateDiscountPolicy로 변경을 해주면 RateDiscountPolicy가 등록이 됩니다.

그 이유는 스프링 빈으로 등록될 때 RateDiscountPolicy이 rateDiscountPolicy로 등록이 되기 때문입니다.

만약 FixDiscountPolicy를 등록하고 싶으면 fixDiscountPolicy로 변경하면 됩니다.

 

@Qualifier

@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {
	//코드
 }
 
 @Component
 @Qualifier("mainDiscountPolicy")
 public class RateDiscountPolicy implements DiscountPolicy {
	//코드
 }
 
 
 @Component
public class OrderServiceImpl implements OrderService {
	
    
	private final DiscountPolicy discountPolicy;
    
    @Autowired
    public OrderServiceImpl(@Qualifier("fixDiscountPolicy") DiscountPolicy discountPolicy) {
    		this.discountPolicy = discountPolicy;
    }

DiscountPolicy를 구현하고 있는 구현체 클래스에 @Qualifier("이름")을 설정해주면 됩니다.

여기서 "이름" 이 부분에 적히는 이름이 스프링 빈에 저장되는 것이 아닙니다.

이해하기 쉽게 설명하자면 별명 같은 거라고 생각하면 쉽습니다.

 

구현체 클래스에 @Qualifier("이름")를 설정해줬으면 생성자 주입 매개변수 타입 앞에 똑같이 적어줍니다.

@Qualifier("fixDiscountPolicy") DiscountPolicy discountPolicy를 하게 되면 FixDiscountPolicy가 빈에 등록됩니다.

RateDiscountPolicy를 등록하고 싶으면 fixDiscountPolicy -> mainDiscountPolicy로 변경해주면 됩니다.

 

@Primary 

- @Primary 어노테이션 붙은 구현체가 우선순위가 됩니다.

 

@Component
public class FixDiscountPolicy implements DiscountPolicy {
	//코드
 }
 
 @Component
 @Primary
 public class RateDiscountPolicy implements DiscountPolicy {
	//코드
 }
 
 
 @Component
public class OrderServiceImpl implements OrderService {
	
    
	private final DiscountPolicy discountPolicy;
    
    @Autowired
    public OrderServiceImpl(DiscountPolicy discountPolicy) {
    		this.discountPolicy = discountPolicy;
    }

RateDiscountPolicy 구현체에 @Primay 어노테이션을 쓰게 되면 RateDiscountPolicy의 우선순위가 제일 높아서

생성자 주입에는 @Primary가 붙은 RateDiscountPolicy가 들어가게 되면서 빈에는 RateDiscountPolicy가 등록이 됩니다. FixDiscountPolicy가 RateDiscountPolicy보다 우선순위를 높게 하고 싶으면 RateDiscountPolicy에 있는 @Primary를 지우고 FixDiscountPolicy에 붙이면 됩니다.