의존성 후보 선택
같은 PasswordHasher 타입의 현재·레거시 구현을 @Primary, @Qualifier, 컬렉션 주입으로 선택하고 선택 근거를 코드에 남깁니다.
같은 타입의 빈이 둘 이상이면 컨테이너는 하나를 임의로 고르지 않습니다.
게시판이 새 비밀번호 해시와 이전 해시를 함께 읽는 마이그레이션 기간을 예로 선택 방법을 구분합니다.
Completion과 FocusTime 모두 유효하지만 plain PasswordHasher 하나를 요구하면 후보 집합을 줄일 수 없다.
- PasswordHasher 요청
단일 의존성 계약
- Completion 후보
고정 10점 구현
- FocusTime 후보
시간 비례 구현
- 모호성
NoUniqueBeanDefinitionException
기본 후보 @Primary
새 회원가입은 현재 해시 구현을 사용하고, 기존 회원 로그인만 레거시 해시를 확인한다고 가정합니다.
@Bean
@Primary
PasswordHasher currentPasswordHasher() {
return new Pbkdf2PasswordHasher(currentSettings);
}
@Bean
PasswordHasher legacyPasswordHasher() {
return new LegacyPasswordHasher(legacySettings);
}평범한 PasswordHasher 생성자 매개변수에는 @Primary가 붙은 현재 구현이 주입됩니다.
@Primary는 “대부분의 주입 지점이 사용할 기본값”을 표현합니다.
의미를 지정하는 @Qualifier
레거시 회원을 검증하는 전용 객체는 선택 이유를 매개변수에 남깁니다.
@Bean
LegacyPasswordVerifier legacyPasswordVerifier(
@Qualifier("legacyPasswordHasher")
PasswordHasher passwordHasher
) {
return new LegacyPasswordVerifier(passwordHasher);
}문자열 오타가 걱정되면 사용자 정의 한정 애노테이션을 만들 수 있습니다.
@Target({FIELD, PARAMETER, METHOD})
@Retention(RUNTIME)
@Qualifier
public @interface LegacyHash { }대부분이 쓰는 기본값과 특정 역할이 요구하는 구현을 같은 표식으로 해결하지 않는다.
- plain 주입인가
Primary가 기본 후보
- 집중 분석 역할인가
focus-time Qualifier로 지정
- 요청마다 바뀌는가
전체 후보를 registry로 전달
- 모두 실행하는가
ordered collection을 순회
모든 후보 받기
마이그레이션 상태를 점검하는 관리 도구처럼 모든 구현이 필요한 경우 컬렉션을 주입합니다.
public final class PasswordHasherInventory {
private final List<PasswordHasher> hashers;
public PasswordHasherInventory(List<PasswordHasher> hashers) {
this.hashers = List.copyOf(hashers);
}
public int implementationCount() {
return hashers.size();
}
}List<PasswordHasher>는 후보가 여러 개인 상황을 의도적으로 받습니다.
반면 회원가입 서비스는 하나만 필요하므로 컬렉션을 받아 스스로 구현을 고르게 하지 않습니다.
factory method 이름이 바뀌어도 focus-time이라는 주입 계약은 custom annotation에 남는다.
- PolicyMode
Qualifier를 확장한 업무 annotation
- candidate
focus-time 값을 구현에 표시
- injection point
같은 값을 요구해 후보를 축소
- refactor
빈 method 이름 변경과 독립
선택 기준 비교
| 방법 | 적합한 상황 | 주의점 |
|---|---|---|
| 단일 타입 | 후보가 정확히 하나 | 둘이면 시작 실패 |
@Primary | 애플리케이션 기본 구현 | 기본 의미가 분명해야 함 |
@Qualifier | 특정 역할·레거시 경로 | 문자열보다 의미 이름 사용 |
List<T> | 모든 구현을 순회 | 서비스가 임의 선택하지 않기 |
기본 하나·고정 역할 하나·runtime 전체 선택은 서로 다른 객체 graph를 만든다.
| 요구 | 도구 | 결정 시점 |
|---|---|---|
| 기본 후보 하나 | Primary | context 조립 |
| 특정 역할 하나 | Qualifier | injection point 해결 |
| 요청 key 선택 | List와 registry | 업무 호출 |
연습 문제
운영 프로필에서는 현재 해시만 회원가입 서비스에 주입하고, 마이그레이션 도구는 현재·레거시 구현을 모두 받게 구성하세요.
@Primary를 제거했을 때 회원가입 서비스 생성이 실패하는지도 컨텍스트 테스트로 확인합니다.