객체가 필요한 협력 객체를 직접 new로 만들지 않고, 바깥(스프링 컨테이너)에서 넣어 받는 방식. 무엇을 언제 만들고 어떻게 연결할지의 제어가 내 코드에서 컨테이너로 넘어가므로 제어의 역전(Inversion of Control, IoC)이라고도 부른다.
- 컨테이너가 만들고 관리하는 객체가 빈이다
- 주입 방법은 생성자·세터·필드가 있고, 생성자 주입을 권장한다. 필드를
final로 둘 수 있고 테스트에서 직접 넣기 쉽다 - 컨테이너는 원본 대신 프록시를 주입하기도 한다. @Transactional이 동작하는 이유다
- 프런트 비유: React Context로 의존성을 내려받는 것과 닮았지만, 연결을 컨테이너가 타입으로 찾아 준다는 점이 다르다
주입 방식
@Service
public class OrderService {
private final PaymentClient payments; // final 가능
public OrderService(PaymentClient payments) { // 생성자가 하나면 @Autowired 생략 가능
this.payments = payments;
}
}- 생성자 주입: 의존성이 빠지면 객체를 만들 수조차 없어 누락이 바로 드러난다. 불변으로 둘 수 있고, 테스트에서
new로 가짜를 넣기 쉽다. @RequiredArgsConstructor와 함께 쓰면 코드가 짧다 - 세터 주입: 선택적 의존성에만 쓴다
- 필드 주입(
@Autowired private): 짧지만final을 쓸 수 없고, 컨테이너 없이는 테스트에서 값을 넣기 어렵다
같은 타입의 빈이 여럿일 때
주입할 후보가 둘 이상이면 스프링은 다음 순서로 고른다.
- 주입 지점의
@Qualifier("이름")가 가리키는 빈 - 후보 중
@Primary가 붙은 빈 - 파라미터·필드 이름과 같은 이름의 빈
셋 다 없으면 NoUniqueBeanDefinitionException으로 시작에 실패한다. 이름 일치에 기대는 방식은 변수명을 바꾸면 조용히 깨지므로, 의도가 있다면 @Qualifier나 @Primary로 드러낸다. 전부 필요하면 List<PaymentClient>로 받는다(전략 패턴).
순환 참조
A가 B를, B가 A를 생성자로 요구하면 어느 쪽도 먼저 만들 수 없어 시작 시 실패한다. 필드·세터 주입이면 예전에는 우회됐지만, 스프링 부트 2.6부터는 이런 순환 참조도 기본으로 금지된다. 대개 책임이 잘못 나뉜 신호라, 공통 부분을 세 번째 빈으로 빼거나 이벤트로 끊는다(결합도, 의존성 역전 원칙).
출처: Spring Framework 문서: Dependency Injection · @Qualifier로 미세 조정