Spring에서 같은 타입의 Bean이 여러 개 등록되어 있을 때, @Autowired로 주입하려고 하면 어떤 Bean을 주입해야 할지 알 수 없어 오류가 발생합니다.
// Food 인터페이스
public interface Food {
void eat();
}
// Chicken 클래스
@Component
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
// Pizza 클래스
@Component
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
@SpringBootTest
public class BeanTest {
@Autowired
Food food; // 오류 발생! Chicken과 Pizza 중 어떤 것을 주입해야 할지 모름
}
오류 메시지: "Food 타입의 Bean 객체가 하나 이상 있습니다."
필드명을 Bean의 이름과 동일하게 지정하면 해당 Bean이 주입됩니다.
@SpringBootTest
public class BeanTest {
@Autowired
Food pizza; // Pizza Bean 주입
@Autowired
Food chicken; // Chicken Bean 주입
}
원리: @Autowired는 먼저 타입(Type)으로 Bean을 찾고, 타입이 여러 개면 이름(Name)으로 찾습니다.
여러 Bean 중 우선적으로 주입될 Bean을 지정할 수 있습니다.
@Component
@Primary // 같은 타입의 Bean이 여러 개일 때 우선 주입
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
@SpringBootTest
public class BeanTest {
@Autowired
Food food; // Chicken Bean이 주입됨
}
특징: 별도의 설정 없이 기본적으로 주입될 Bean을 지정할 때 유용합니다.
특정 Bean을 명시적으로 지정하여 주입받을 수 있습니다.
@Component
@Qualifier("pizza") // Bean의 별칭 지정
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("pizza") // pizza라는 이름의 Bean을 주입
Food food;
}
특징: 주입받는 쪽에서도 @Qualifier를 명시해야 하므로, 특정 상황에서만 사용하는 Bean에 적합합니다.
@Component
@Primary
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
@Component
@Qualifier("pizza")
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("pizza")
Food food;
@Test
@DisplayName("Primary와 Qualifier 우선순위 확인")
void test1() {
food.eat(); // "피자를 먹습니다." 출력
}
}
결론: @Qualifier가 @Primary보다 우선순위가 높습니다.
@Autowired는 타입 → 이름 순서로 Bean을 찾습니다.필드 주입보다 생성자 주입이 권장되는데, 생성자 주입에서도 동일하게 적용됩니다.
@Service
public class FoodService {
private final Food food;
// 생성자 주입에서 @Qualifier 사용
public FoodService(@Qualifier("pizza") Food food) {
this.food = food;
}
}
같은 타입의 Bean을 모두 받아서 사용하고 싶다면 List로 주입받을 수 있습니다.
@Service
public class FoodService {
private final List foods;
public FoodService(List foods) {
this.foods = foods; // Chicken, Pizza 모두 주입됨
}
public void eatAll() {
foods.forEach(Food::eat);
}
}
이 방법은 전략 패턴이나 여러 구현체를 동시에 사용해야 할 때 유용합니다.