@Autowired 에서 조회되는 빈이 2개 이상일 때
@Autowired
- 타입을 기준으로 조회하기 때문에 같은 타입의 빈이 2개 이상이면 `@NoUniqueBeanDefinitionException` 오류가 발생한다.
조회 대상 빈이 2개 이상일 때 해결 방법
- @Autowired 필드명 또는 파라미터명을 이용한 매칭 (거의 사용 X)
- @Qualifier 를 이용한 명확한 구분
- @Primary 를 이용한 기본 빈 설정
@Autowired 필드명 또는 파라미터명을 이용한 매칭 (거의 사용 X)
- 타입 매칭 시도 > 필드명 / 파라미터 명으로 매칭 시도
@Autowired
private DiscountPolicy rateDiscountPolicy
@Qualifier 를 이용한 명확한 구분
- 추가적인 구분자를 붙여주는 방법 (실제 빈 이름 변경 X, 단순한 문자열 태그 개념)
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
빈 등록 시 @Qualifier 를 붙여주고,
주입 시에 @Qualifier 와 함께 등록한 이름을 적어준다.
만약 @Qualilfier("등록한 이름") 을 찾지 못하면 등록한 이름을 가진 스프링 빈을 찾는다.
(위에 코드에서 보면, 빈 이름이 mainDiscountPolicy 인 스프링 빈을 찾는다.)
하지만 의도를 명확히 하기 위해 빈 이름 대신 @Qualifier를 직접 지정하는 것이 좋다.
단점은 모든 코드에 @Qualifier 를 붙어야 한다는 점이다.
> 참고 <
- @Qualifier 는 직접 스프링 빈을 등록할 때도 사용할 수 있다.
@Primary 를 이용한 기본 빈 설정
- 특정 빈을 우선적으로 선택하도록 지정하는 방법이다.
- 코드 수정 없이 기본 정책을 변경할 때 유용하다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Primary + @Qualifier
- @Primary는 기본 선택, @Qualifier는 명확한 우선순위 지정한다.
- 두 개를 조합하면 더욱 깔끔한 코드 작성 가능하다.
@Component
@Primary
public class MainDatabaseConnection implements DatabaseConnection {}
@Component
@Qualifier("subDB")
public class SubDatabaseConnection implements DatabaseConnection {}
@Autowired
private DatabaseConnection databaseConnection; // @Primary가 적용된 빈이 자동 주입
@Autowired
public void setDatabaseConnection(@Qualifier("subDB") DatabaseConnection databaseConnection) {
this.databaseConnection = databaseConnection; // 특정 빈을 명확히 주입
}
우선순위
@Primary : 기본값처럼 동작
@Qualifier : 매우 상세하게 동작 (우선순위 높음)
> 참고 <
- 일반적으로 스프링은 자동보다는 수동이, 넓은 범위의 선택권보다는 좁은 범위의 선택권이 우선순위가 높다.
애노테이션 직접 만들기
@Qualifier("이름") 의 문제점
- 컴파일 시 타입 체크 할 수 없다.
- 이름은 문자열이므로 오타가 발생해도 컴파일 오류가 발생하지 않는다.
따라서 커스텀 애노테이션을 만들면 안정성을 높일 수 있다.
애노테이션 생성 방법
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
@Qualifier를 포함한 새로운 애노테이션을 생성한다.
`@Target` : 애노테이션 적용 위치 지정
`@Retention(RetentionPolicy.RUNTIME)` : 런타임시에도 유지되도록 설정
`@Qualifier("이름")` : Quilfier 역할 수행
@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
스프링 빈을 등록할 때 만든 커스텀 애노테이션을 붙이면
@Autowired 주입 시 @MainDiscountPolicy 를 사용하여 안전한 타입 체크가 가능해진다.
출처 | 스프링 핵심 원리 - 기본편(김영한) - 인프런
'💠프로그래밍 언어 > Java' 카테고리의 다른 글
[Spring] 빈 생명주기 콜백 (@PostConstruct, @PreDestroy) (0) | 2025.02.26 |
---|---|
[Spring] 특정 타입의 빈을 동적으로 가져오는 방법 (0) | 2025.02.26 |
[Spring] 롬복 라이브러리의 편리한 기능들 (0) | 2025.02.26 |
[Spring] 스프링 컨테이너 (애노테이션 기반, XML 기반) (1) | 2025.02.26 |
[Spring] 스프링 빈 조회하기, 스프링 빈 설정 메타 정보(BeanDefinition) (0) | 2025.02.25 |