Spring 자동 의존 관계 주입 시 빈이 2개 이상일 때

강정우·2023년 11월 16일
0

Spring-boot

목록 보기
16/73

조회 빈이 2개 이상일 때

  • 앞서 우리는 @Autowired는 타입으로 조회하는 것을 배웠다.
    그래서 마치 @Autowired는 ac.getBean() 함수처럼 동작한다고 이해한 바가 있다.

  • 하지만 역시 타입으로 조회할 땐 하위 타입의 빈이 2개이상이 존재할 경우에는 문제가 발생한다.
    그럼 DisountPolicy의 하위타입인 FixDiscountPolicyRateDiscountPolicy를 스프링 빈으로 선언해보자.

  • 그러면 아래와 같이 NoUniqueBeanDefinitionException 에러가 터질 것이다.
    그렇다면 이렇게 생각할 수 있다.
    -> 아~니 그냥 더 하위타입인 FixDiscountPolicyRateDiscountPolicy 로 주입하면 되는거 아님?
    -> 어리석은 소리! 그러게 되면 DIP를 위반하고 유연성이 더 떨어진다. 왜?
    -> 하위타입으로 고정되어버리니까 역할과 구현에서 DIP를 위반하기 때문이다.
    항상 역할(인터페이스)에 의존해야지 구체적인것(~~impl)에 의존하면 안 된다고 했다.

  • 그럼 어떻게 해결함? -> 물론 수동으로 하면 되긴 하지만 자동 의존관계 주입으로도 해결할 수 있는 방법이 3가지 정도 존재한다.

1. @Atowired 필드 명 매칭

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

// 필드 명을 빈으로 이름 변경한 코드
@Autowired
private DiscountPolicy reateDiscountPolicy
  • 필드명이나 생성자 함수같은 경우에는 파리미터 이름을 보고 자동으로 결정하도록 하는 것이다.

spring은 getBean을 하면 하위 자식 타입까지 모두 조회한다고 하였는데 이때 1차로 타입 매칭이 진행되고 타입 매칭 결과가 2개 이상일 경우 필드 명, 혹은 파라미터 명으로 빈 이름을 매칭해준다.

2. @Qualifier -> @Qualifier 끼리 매칭 -> 빈 이름 매칭

  • @Qualifier는 추가 구분자를 붙여주는 방식이다. 주입시 추가적인 방법을 제공하는 것이지 빈 이름을 변경하는 것은 아니다.

사용법 1. 컴포넌트 선언 시 이름 부여

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

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

사용법 2. 주입시 @Qualifier를 필드명, 파라미터명 앞에 추가

...
  pricate final DiscountPolicy discountPolicy;

  @Autowired
  public OrderServiceImpl(@Qualifier("rateDiscountPolicy")DiscountPolicy discountPolicy){
      this.discountPolicy = discountPolicy
  }
  ...
  • 이때 같은 이름의 qualifier를 못 찾는다면 다음으로는 부여된 같은 이름의 bean을 찾기 시작한다.
    그런데 이는 이름이 2번부여된것 같기도 하고 다른 개발자가 봤을 때 혼동을 줄 수 있기 때문에 이런 부분은 되도록이면 사용을 지양하는 것이 좋다.

3. @Primary 사용

  • 보통 자주 사용하는 방법이다. 매우 편하긴한데 한계점 또한 명확히 존재한는 부분이다.
// RateDiscountPolicy.class
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy{...}

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

이렇게 사용하고 beanScan에 여러개가 걸리면 @Primary 가 붙은 이거 하나를 가져온다는 것이다.
예를 들어 굉장히 다양한 DB가 있다고 가정할 때 mainDB에 이걸 붙이면 된다. 그리고 만약 primary가 아닌 다른 DB를 갖고오고 싶을 땐 그때 Qualifier를 쓴다든지, 직접 이름을 지정하여 갖고오든지 하면 된다.

  • 그렇다면 @Primary@Qualifier가 같이 붙어있다면 뭐가 먼저일까? => 항상 자동보다는 수동이, 넓은 선택보다는 자세한 선택이 우선되기 때문에 Qualifier가 더 우선시 된다.
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글