@Autowired도 ac.getBean(XXX.class)처럼 Type으로 조회하기 때문에
해당 타입 빈이 유일하지 않다면 문제다.
예시)
만약 생성자에서 DiscountPolicy를 주입받는 상황이라고 하자. (by @Autowired)
그런데 DiscountPolicy의 하위 클래스 중 @Component인 클래스가 2개일 때,
그러니까 FixDiscountPolicy와 RateDiscountPolicy로 2개가 스프링빈으로 등록되어있을 때,
@Autowired 의존관계 자동 주입시 NoUniqueBeanDefinitionException 오류가 발생한다.
이러한 의존관계 자동 주입시 동일타입 빈 문제를 어떻게 해결할까?
3가지 방법이 있는데, @Primary를 많이 사용한다.
나머지 방법들도 사용할 상황이 있으까 이런게 있구나하고 알아두자.
@Autowired 타입매칭시 여러 빈이 있을 때 해결하는 방법
1. 필드이름, 파라미터 이름으로 빈을 매칭한다.
2. @Qualifier 사용 → @Qualifier에서 없으면 빈이름으로 매칭 시도
3. @Primary 사용
cf. @Primary는 기본값처럼 동작한다. 따라서 비교적 수동 설정이 필요한 @Qualifier가 우선권이 높다.
특정 타입 빈을 모두 조회하고 싶을 수도 있다.
예를 들면 DiscoutPolicy를 모두 조회해서
user에게 FixDiscountPolicy, RateDiscountPolicy에 대한 선택권을 주고 싶을 경우..
∨ 특정 타입 빈을 Map, List를 통해 모두 조회하는 예시
아래 코드를 보자.
컨테이너 ac에 방금 새로 작성한 DiscountService를 전달했다.
DiscountService의 생성자에 @Autowired를 사용했고, 그 파라미터에는 Map과 List를 작성했다.
Map의 value와 List 타입은 DiscountPolicy로 지정했다.
이 경우 모든 하위타입이 잘 @Autowired되어서,
모든 DiscountPolicy 하위타입이 Map, List로 쭈루룩 나타난다.
public class AllBeanTest {
@Test
void findAllBean() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
}
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
@Autowired
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
System.out.println("policyMap = " + policyMap);
System.out.println("policies = " + policies);
}
}
}
즉, 결과가 다음과 같이 나타난다.
DiscountPolicy 타입을 @Autowired 했을 때 하위타입이 Map, List로 잘 표현되었다.
policyMap = {fixDiscountPolicy=hello.core.discount.FixDiscountPolicy@246f8b8b, rateDiscountPolicy=hello.core.discount.RateDiscountPolicy@278bb07e}
policies = [hello.core.discount.FixDiscountPolicy@246f8b8b, hello.core.discount.RateDiscountPolicy@278bb07e]
Test코드를 작성해보자.
∨ 매개변수에 멤버, 가격, 할인정책key를 넣으면 이전에 만든 Map<String, DiscountPolicy> policyMap에서 해당 정책을 꺼내 discountPrice를 꺼내주는 메서드인 DiscountService.discount를 구현했다.
cf. TDD로 구현해보았다.
∨ 그리고 assertThat(discountPrice).isEqualTo(1000), assertThat(ratediscountPrice).isEqualTo(2000)을 통해서 각각의 할인 정책이 잘 기능하는지 검증완료했다.
public class AllBeanTest {
@Test
void findAllBean() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
DiscountService discountService = ac.getBean(DiscountService.class);
Member member = new Member(1L, "userA", Grade.VIP);
int discountPrice = discountService.discount(member, 10000, "fixDiscountPolicy");
assertThat(discountService).isInstanceOf(DiscountService.class);
assertThat(discountPrice).isEqualTo(1000);
int rateDiscountPrice = discountService.discount(member, 20000, "rateDiscountPolicy");
assertThat(rateDiscountPrice).isEqualTo(2000);
}
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
@Autowired <- 생성자가 하나기 때문에 생략가능
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
System.out.println("policyMap = " + policyMap);
System.out.println("policies = " + policies);
}
public int discount(Member member, int price, String discountCode) {
DiscountPolicy discountPolicy = policyMap.get(discountCode);
return discountPolicy.discount(member, price);
}
}
}
∨ 동적으로 빈을 선택해야 할 때 이런식으로 Map과 다형성을 사용하면 좋다
'web +a' 카테고리의 다른 글
자동/수동 빈에 대한 올바른 선택 (0) | 2022.07.14 |
---|---|
annotation 직접 만들기 (@Qualifier 관련) (0) | 2022.07.13 |
생성자 코드 최적화 - lombok 라이브러리 (0) | 2022.07.13 |
생성자 주입 권장 (0) | 2022.07.13 |
자동주입 - 옵션 처리 (0) | 2022.07.11 |