일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- Today
- Total
- 스프링 싱글톤
- DI컨테이너
- 스프링 빈
- beandefinition
- Spring
- 롬복 Qualifier
- 라즈베리파이
- 스프링
- Spring interceptor
- 스프링 빈 조회
- 스프링 컨테이너
- ComponentScan
- UsernamePasswordAuthenticationFilter
- autowired
- docker
- 생성자 주입
- 싱글톤 컨테이너
- 도커
- 의존관계 주입
- 라즈베리파이4
- 빈 중복 오류
- springsecurity
- DI
- Servlet Filter
- Autowired 옵션
- qualifier
- RequiredArgsConstructor
- HandlerMethodArgumentResolver
- 객체지향
- 스프링 Configuration
그날그날 공부기록
스프링 빈 중복 시 오류 & 해결방법 본문
@Autowired를 사용하여 빈을 조회하면 타입이 기준이 된다.
의존관계를 주입받을 경우 DIP를 위해 구현 객체가 아닌 인터페이스를 사용한다.
하지만 2개 이상 빈의 타입이 같으면 오류가 발생한다.
다음과 같이 할인을 위한 DiscountPolicy 인터페이스와 구현 객체가 2개 있다고 생각해보자.
두 객체 모두 @Component
를 작성해 스프링 빈으로 스캔되고 등록된다.
이 상황에서 어떤 서비스에서 상위 타입인 DiscountPolicy로 의존관계를 주입받게 된다면 같은 타입의 빈이 2개가 되기 때문에 NoUniqueBeanDefinitionException오류가 발생한다.
물론 구체적인 타입인 RateDiscountPolicy로 의존관계를 주입받는다면 에러는 발생하지 않겠지만 DIP를 위반하게 된다.
해결방법
1. @Autowired
를 사용했다면 필드 명을 빈 이름으로 변경하면 된다.
→ 중복된 타입의 빈이 검색됐을 경우 이름이 같은 필드가 있다면 해당 빈이 주입된다.
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy){
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
다음과 같이 DiscountPolicy를 주입받는다면 에러가 난다.
하지만 필드 이름을 사용하려는 빈 이름인 rateDiscountPolicy 혹은 fixDiscountPolicy로 변경한다면 그 이름에 맞는 빈이 주입된다!
2. @Qualifier
사용하여 스프링이 빈을 매칭 할 수 있는 방법을 제공해준다.
빈으로 등록할 객체와 의존관계를 주입받는 필드에 @Qualifier(”빈 이름")
을 적어주면 해당 빈 이름에 맞는 빈을 주입해준다.
//빈 등록 객체
@Component
@Qualifier("rate")
public class RateDiscountPolicy implements DiscountPolicy{
...
}
//DiscountPolicy를 사용하는 서비스
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("rate") DiscountPolicy discountPolicy){
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
이렇게 빈과 필드를 @Qualifier
로 매칭해주면 된다.
빈 이름을 설정한 문자열로 바꾸는 것이 아닌 주입할 빈을 찾을 수 있는 추가적인 수단을 주는것이다.
만약 @Qualifier
로 매칭 되는 빈이 없다면, 그냥 필드명과 동일한 빈을 검색한다.
순서는 다음과 같다. @Qualifier
로 매칭 → 필드 이름 → 에러
3. @Primary
를 사용한다.
빈으로 등록할 객체에 해당 애노테이션을 적어주면 빈 타입 중복 시 @Primary
객체를 우선 주입한다.
다음과 같이 중복되는 타입의 빈이 2개일 경우 @Primary
가 붙은 RateDiscountPolicy가 선택되어 주입된다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy{
...
}
@Component
public class FixDiscountPolicy implements DiscountPolicy{
...
}
+ 자동 생성된 생성자에 Qualifier추가하기
@RequiredArgsConstructor
을 사용할 경우 @Qualifier
가 적용되지 않는다.
이유는 @RequiredArgsConstructor
는 애노테이션까지 포함시켜 생성자를 만들지 않기 때문이다.
하지만 포함시킬 수 있는 방법이 있다!
- src/main/java에 lombok.config 파일을 생성한다. → src/main/java/lombok.config
- lombok.config 파일에 다음 코드를 추가한다.
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
- 프로젝트를 다시 컴파일 한다.
Intellij를 사용한다면 out이라는 폴더를 삭제하고 해야한다.
gradle은 gradlew clean을 하고 실행한다.
@Component
@Qualifier("rate")
public class RateDiscountPolicy implements DiscountPolicy{
...
}
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
@Qualifier("rate")
private final DiscountPolicy discountPolicy;
...
}
설정파일을 따로 적용했으므로 생성자에 @Qualifier
가 추가되어 생성된다.
출처
'Spring 공부' 카테고리의 다른 글
Map, List에 빈 주입 & 사용하기 (0) | 2022.08.09 |
---|---|
Qualifier을 대체할 수 있는 커스텀 애노테이션 만들기 (0) | 2022.08.09 |
lombok(롬복) 설치 & 적용하기 (0) | 2022.08.02 |
왜 생성자 주입 방식을 선택해야 할까? (0) | 2022.07.28 |
@Autowired 옵션 처리 (0) | 2022.07.28 |