3장 : 컨테이너·빈·생명주기·스코프
의존성 후보 선택
같은 PasswordHasher 타입의 현재·레거시 구현을 @Primary, @Qualifier, 컬렉션 주입으로 선택하고 선택 근거를 코드에 남깁니다.
같은 타입의 빈이 둘 이상이면 컨테이너는 하나를 임의로 고르지 않습니다. 게시판이 새 비밀번호 해시와 이전 해시를 함께 읽는 마이그레이션 기간을 예로 선택 방법을 구분합니다.
기본 후보 @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 { }모든 후보 받기
마이그레이션 상태를 점검하는 관리 도구처럼 모든 구현이 필요한 경우 컬렉션을 주입합니다.
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>는 후보가 여러 개인 상황을 의도적으로 받습니다.
반면 회원가입 서비스는 하나만 필요하므로 컬렉션을 받아 스스로 구현을 고르게 하지 않습니다.
선택 기준 비교
| 방법 | 적합한 상황 | 주의점 |
|---|---|---|
| 단일 타입 | 후보가 정확히 하나 | 둘이면 시작 실패 |
@Primary | 애플리케이션 기본 구현 | 기본 의미가 분명해야 함 |
@Qualifier | 특정 역할·레거시 경로 | 문자열보다 의미 이름 사용 |
List<T> | 모든 구현을 순회 | 서비스가 임의 선택하지 않기 |
연습 문제
운영 프로필에서는 현재 해시만 회원가입 서비스에 주입하고, 마이그레이션 도구는 현재·레거시 구현을 모두 받게 구성하세요.
@Primary를 제거했을 때 회원가입 서비스 생성이 실패하는지도 컨텍스트 테스트로 확인합니다.