일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 의존성 주입
- Security
- Setter
- vuex
- Stateless
- javascript
- Vue.js
- di
- vue-cli
- JPA
- dependency injection
- Excel
- js
- 라이프 사이클
- VUE
- thymeleaf
- Java
- HTTP
- Kotlin
- cache
- 로그인
- Singleton
- HTTP 메서드
- 캐시
- DB
- 프로토타입
- Repository
- BEAN
- 싱글톤
- Spring
- Today
- Total
jhhan의 블로그
스프링 코어(12) - 조회 빈이 여러 개인 경우 본문
이번 포스트에서는 제목처럼 2개 이상의 빈이 있을 때 어떻게 처리하는지 알아보겠습니다.
원래 스프링 빈은 하나가 등록되어야 합니다.
하지만 인간이기에 스프링 빈을 2개 이상 등록하는 실수를 할 수 있습니다.
그러면 에러가 발생하겠죠..
이런 에러가 발생하는 것을 막는 법에 대해 알아보겠습니다.
일단 먼저 스프링 빈이 2개 등록되었을 경우를 알아보겠습니다.
저는 DiscountPolicy 인터페이스로 2개의 구현체를 만들었습니다.
그 중 RateDiscountPolicy에만 @Component 애노테이션이 있었기 때문에
OrderServiceImpl을 실행하는데 문제가 없었습니다.
근데 FixDiscountPolicy에도 애노테이션을 넣는다면 어떻게 될까요?
FixDiscountPolicy에도 추가를 한 다음 테스트를 돌려봅니다.
그러면 이전에 만들어둔 AutoAppConfigTest에서 에러가 생기는 것을 확인할 수 있습니다.
에러 내용은
"expected single matching bean but found 2" 라는 문구를 볼 수 있습니다.
한 개의 빈을 예상했지만 2개를 발견했다고 합니다.
@Component를 두군데에 넣어놨기 때문에 이런 에러가 발생했습니다.
애노테이션을 추가하더라도 코드는 문제없이 잘 돌아가야 합니다.
그러면 문제없이 돌아가도록 해결책을 알아보겠습니다.
1. 필드명 매칭
파라미터 이름을 빈 이름으로 진행합니다.
설명이 애매하므로 바로 코드에 적용해 보겠습니다.
OrderServiceImpl 클래스를 다음과 같이 수정합니다.
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy rateDiscountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = rateDiscountPolicy;
}
...
}
실행한다면 아까 에러가 발생했던 AutoAppConfigTest 클래스에서는 더 이상 에러가 발생하지 않는다는 것을 알 수 있습니다.
필드명 매칭은 먼저 빈 타입 매칭을 진행한 후 여러 개가 검색되면 추가로 동작되는 기능입니다.
- 빈 타입 매칭
- 여러 개의 빈 타입 매칭 → 필드명(파라미터 명)으로 빈 이름 매칭
필드명 매칭을 진행하려면 기존에 선언했던 클래스와 이름을 아주 비슷하게 해야합니다.
- RateDiscountPolicy → rateDiscountPolicy
- FixDiscountPolicy → fixDiscountPolicy
cf) 참고로 필드명 주입으로 전체 테스트 코드를 실행한다면 AutoAppConfigTest는 통과하지만,
다른 코드에서는 에러가 납니다. ( UnsatisfiedDependencyException )
왜냐하면, 파라미터 명이 바뀌어서 빈을 제대로 못 찾기 때문입니다... (아마도...)
2. @Qualifier
Qualifier라는 추가 구분자를 붙여주는 방법입니다.
단, 빈 이름을 변경하는 방법은 아닙니다.
각 클래스에 다음과 같은 @Qualifier를 붙여줍니다.
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
System.out.println("Constructor: memberRepository = " + memberRepository);
System.out.println("Constructor: discountPolicy = " + discountPolicy);
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
그리고 OrderServiceImpl를 다음과 같이 수정합니다.
코드 수정 후 테스트를 돌려보면 에러가 발생하지 않은 것을 확인할 수 있습니다.
cf) 그리고 Qualifier는 생성자 주입과 수정자 주입에서 모두 사용이 가능합니다.
@Qualifier를 사용하면 옆에 설정한 이름을 찾아서(여기서는 mainDiscountPolicy) 주입합니다.
만약 mainDiscountPolicy를 못 찾으면..
mainDiscountPolicy라는 이름을 갖는 스프링 빈을 추가로 찾습니다.
하지만, 왠만하면 설정한 이름을 찾게 코드를 짜는 것이 이롭다고 생각합니다.
@Qualifier 정리
- Qualifier 끼리 매칭
- 다음으로 빈 이름 매칭
- 둘 다 없으면 NoSuchBeanDefinitionException
3. Primary
@Primary를 사용하고 이 방법은 우선순위를 정하는 것과 비슷합니다.
여러 개의 빈이 검색되는 경우 @Primary가 붙어있는 것이 우선적으로 사용됩니다.
코드로 바로 살펴보겠습니다.
2개의 클래스를 위와 같이 변경합니다.
그리고 OrderServiceImpl을 위와 같이 변경합니다.
테스트를 돌려보면.. 오류없이 모두 통과하는 것을 알 수 있습니다.
@Primary 정리
- 빈 여러 개 검색 시
- @Primary가 붙은 녀석이 사용된다.
이렇게 3가지를 살펴보았고,
3개 중에서 Primary를 사용하는 것이 가장 편한 것을 알 수 있습니다.
사용 측면에서는 Primary와 Qualifier가 많이 사용된다고 하네요.
@Qualifier를 사용하게 되면 @Primary와 달리 더 많은 코드를 작성해야 하고,
모든 코드에 @Qualifier를 붙여야 하는 단점도 있습니다.
그래도 Qualifier를 사용하는 이유가 있겠죠 ㅎㅎ
@Primary는 우선권만 부여하는 방식이기 때문에 동작 방식이 간단합니다.
@Qualifier는 코드를 많이 작성하는 대신 자세하게 동작하는 방식을 설정할 수 있습니다.
보통 더 자세하게 조작할 수 있는 방식이 먼저 사용되는 편입니다.
→ @Primary와 @Qualifier를 같이 사용하게 된다면 @Qualifier가 사용된 쪽이 우선권이 높다는 것을 알고 있으면 될 것 같습니다.
@Primary와 @Qualifier를 적당히 잘 사용하면 되겠네요!
이렇게 조회한 빈이 2개 이상인 경우 해결방법에 대해 알아봤습니다.
실제로 2개 이상의 빈을 설정하는 실수는 흔히 할 수 있을 것 같기 때문에
알아놓으면 코드 에러가 발생하는 확률을 많이 낮춰줄 것 같습니다~
출처: 인프런 - 스프링 핵심원리(기본편) by 김영한
'Spring' 카테고리의 다른 글
스프링 코어(13) - 조회 빈이 모두 필요한 경우 (0) | 2021.08.15 |
---|---|
스프링 코어(번외-1) - 애노테이션 직접 만들기 (0) | 2021.08.13 |
스프링 코어(11) - 옵션 처리 (0) | 2021.07.25 |
스프링 코어(10) - 의존관계 주입 (0) | 2021.07.14 |
스프링 코어(9) - 컴포넌트 스캔_2 (0) | 2021.05.13 |