[Spring] NoUniqueBeanDefinitionException

@Autowired를 이용해서 조회하면, 이것은 getBean메소드를 이용해서 타입 조회하는것과 유사하게 동작한다. 그런데 선택된 빈이 2개 이상일 경우 getBean메소드와 동일하게 @Autowired도 NoUniqueBeanDefinitionException에러를 발생시킨다. 아래 코드를 보자.

 

@Autowired
private DiscountPolicy discountPolicy;


//이때 DiscountPolicy메소드를 
//RateDiscountPolicy와 FixDiscountPolicy라는 두 개의 클래스가 상속받고 있다고 하자.
@Component
public class FixDiscountPolicy implements DiscountPolicy() {...}

@Component
public class RateDiscountPolicy implements DiscountPolicy() {...}

가정에 따라 DiscountPolicy를 두 개의 클래스가 상속받고 있고, 명시해주지 않았으므로 스프링에서 '스프링 빈 조회시 부모 타입으로 조회하면 모든 자식 타입도 함께 조회하는' 성질에 따라서 두 자식 클래스를 모두 조회한다.

그런데 두가지의 자식클래스가 모두 빈으로 등록되어 있으므로 스프링은 어떤 것을 조회해야 할지 알 수 없기 때문에 NoUniqueBeanDefinitionException을 발생시키게 된다.

 

 

해결

해결을 위해서 먼저 @Autowired의 속성을 이용해보자. @Autowired는 기본적으로 타입매칭을 이용한 조회를 시도하지만, 여러 빈이 있으면 필드명, 파라미터명 등으로 추가 매칭을 시도하고 없으면 상기한 에러를 발생시킨다.

따라서 필드명을 빈 이름으로 변경해주기만 해도 에러는 발생하지 않는다.

@Autowired
private DiscountPolicy rateDiscountPolicy;

타입매칭에서 중복검색 오류가 발생했겠지만, 필드명인 rateDiscountPolicy로 빈을 매핑 시도하면 rateDiscountPolicy스프링 빈을 조회할 수 있기 때문에 에러는 발생하지 않는다.

 

 

@Qualifier

또한 아래와 같이 @Qualifier 어노테이션을 이용하여 매칭할수도 있다.

@Autowired
@Qualifier("mainDiscountPolicy")
private DiscountPolicy discountPolicy;


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

다만 이러한 방법은 컴파일타임에 문자열 에러체크가 불가능하기때문에 애노테이션을 별도로 만들고 그 안에 @Qualifier를 적어서 어노테이션을 명시해주면 별도의 설정 어노테이션으로 매칭하는 방법도 사용할 수 있다.

 

 

@Primary

마지막으로 @Primary어노테이션을 추가해주어 자식 클래스 사이에 우선순위을 지정해주는 방법도 있다.

@Autowired
private DiscountPolicy discountPolicy;


@Primary
public class FixDiscountPolicy implements DiscountPolicy() {...}

@Component
public class RateDiscountPolicy implements DiscountPolicy() {...}

이러한 경우 DiscountPolicy타입으로 조회시 스프링은 @Primary가 붙은 FixDiscountPolicy가 무조건 먼저 선택하어 조회한다.