의존관계 주입이란 ?
객체를 직접 생성하는 것이 아닌 스프링 컨테이너에서 빈을 찾아 주입하는 방식으로
특정 구현 클래스에 의존하지 않고 인터페이스를 통해 의존성을 주입받기에
클래스 간의 결합도가 낮아지고 유연성이 높아지게 된다
의존관계 주입 방법은 크게 네가지로 나누어진다
- 수정자 주입
- 일반 메서드 주입
- 필드 주입
- 생성자 주입
일반적으로 의존관계 주입 시
생성자 주입을 지향하고
필드 주입은 지양하여야 한다
이유는 예제의 주문 서비스 구현체를 구현하며 알아보자
의존관계 주입은 @Autowired 애노테이션을 통해 이루어진다
1. 수정자 주입
setter 메서드를 통해 의존관계를 주입하는 방법
선택 및 변경 가능성이 있는 의존관계에 사용한다
@Component
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired(required = false)
public void setMemberRepository(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy){
this.discountPolicy = discountPolicy;
}
}
- required 옵션을 false로 선언하여 주입할 대상이 없어도 동작하도록 선언 가능
- 실행 중간에 인스턴스를 바꾸고 싶다면 외부에서 호출함으로 Application 실행 중 변경 가능
2. 일반 메서드 주입
일반 메서드를 통해 주입받는 방법
@Component
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy){
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- 한번에 여러 필드를 주입 받을 수 있다
- 일반적으로 잘 사용하지 않는다
3. 필드 주입
필드에 @Autowired 애노테이션을 선언하여 주입받는 방법
@Component
public class OrderServiceImpl implements OrderService{
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
코드가 간결하다는 장점이 존재하지만 그 외에 다음과 같은 단점들을 갖고 있다
- 단위 테스트 어려움
- 컨테이너 없이는 의존성 주입이 불가능하다
- 때문에 테스트마다 컨테이너 구동이 필요하다
- 순환 참조 감지 불가
- 필드 주입은 런타임 시점에 리플렉션을 통해 의존성 주입이 이루어지는 특징을 갖고 있다
- 이에 실행 단계에 순환 참조를 감지하지 못하여 어플리케이션이 실행되는 번거로움이 존재한다
- Setter 주입, 필드 주입은 빈 생성 이후에 의존성을 주입한다
- [빈 정의 확인] → [빈 생성 완료] → [필드에 의존성 주입] → [컨테이너 등록]
- 따라서 순환 참조 문제를 미리 감지하기 어렵다
- 순환 참조 자체가 스프링 부트 2.6버전 이후로 개선되었다
- Setter 주입, 필드 주입은 빈 생성 이후에 의존성을 주입한다
- 불변성 보장 불가
- 런타임 시점에 의존관계 주입이 일어나기에 final 키워드를 선언할 수 없다
- 명확하지 않은 의존성
- 의존 관계를 파악하기 위해서는 필드 선언부를 반드시 확인해야 하는 번거로움 존재한다
4. 생성자 주입
생성자 메서드를 통해 의존관계를 주입하는 방법
생성 시점에 한번만 호출되는 것이 보장되며
생성자가 한 개만 있다면 @Autowired 애노테이션을 생략 가능하다
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired // 생략 가능
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
생성자 주입은 필드 주입의 모든 단점이 개선된 형태로 동작한다
- 단위 테스트
- 컨테이너가 없어도 테스트 시점에 명시적으로 의존성을 주입하여 테스트 진행 가능하다
- 순환 참조 감지
- 빈 생성 시점에 의존성이 주입되기에 애플리케이션 실행 시점에 순환 참조를 감지한다
- 순환 참조 발생 시 애플리케이션은 종료된다
- [빈 정의 확인] → [생성자 호출 시 의존성 주입] → [빈 생성 완료] → [컨테이너 등록]
- 빈 생성 시점에 의존성이 주입되기에 애플리케이션 실행 시점에 순환 참조를 감지한다
- 불변성 보장
- final 키워드 선언 가능
- 명확한 의존성
- 생성자를 통해 의존관계를 명확히 알 수 있음
- 누락 방지
- null을 주입하지 않는 한 NPE가 방지된다
위와 같은 이유로 최근 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다
출처
'Spring' 카테고리의 다른 글
Spring Security - 인증 및 권한 부여 (0) | 2024.12.16 |
---|---|
RestControllerAdvice를 통한 예외 관리 (1) | 2024.10.27 |