1. 의존성 주입이란?
의존성 주입(Dependency Injection, DI)은 객체가 자신의 의존성을 외부로부터 주입받는 디자인 패턴으로, 객체의 생성과 의존성 관리를 스프링 컨테이너가 담당한다.
이 방식은 객체가 자신이 필요로 하는 의존성을 직접 생성하거나 조회하지 않도록 함으로써 코드의 결합도를 낮추고, 유연성과 재사용성을 향상시킨다.
2. 의존관계 주입 방법 4가지
- 생성자 주입
- 수정자 주입(setter 주입)
- 필드 주입
- 일반 메서드 주입
실무에서는 주로 생성자 주입과 수정자 주입을 쓰고 필드 주입과 일반 메서드 주입은 거의 쓰지 않는다.
그 이유들을 정리하는게 이번 포스팅의 목표다.
3. 의존성 주입 과정
스프링에서 스프링 빈을 등록하고 의존성을 주입하는 과정은 일반적으로 다음 단계로 이뤄진다.
1️⃣빈 등록
: 스프링 컨테이너는 설정 파일이나 어노테이션을 통해 어떤 클래스를 스프링 빈으로 관리할지 결정하고 등록한다.
이때, @Component, @Service, @Repository, @Controller 등의 어노테이션을 사용한 클래스가 자동으로 검색되어 빈으로 등록된다.
2️⃣ 의존성 주입
: 빈이 등록된 후, 스프링은 빈 간의 의존 관계를 해석하고, 각 빈에 필요한 의존성을 주입한다.
의존성 주입은 주로 빈의 생성 과정에서 이루어지며, 이를 통해 각 객체는 필요한 다른 객체들을 사용할 준비가 된다.
(🔔스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다.
일반 자바 객체에는 @Autowired 어노테이션을 붙여도 의존성 주입이 안된다.)
4. 생성자 주입
- 생성자를 통해서 의존 관계를 주입 받는 방법이다.
✅생성자 주입 예시 코드
@Component
public class OrderServiceImpl implements OrderService{
//final을 붙여서 변수를 불변으로 만든다.
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
//생성자를 통해 구현체를 연결한다.
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
⭐특징
1. 의존성 주입 방법 중 가장 권장되는 방법이다.
2. 생성자 호출시점에 딱 1번만 호출되는 것이 보장된다.
3. 한번 생성되고 나면 못바꾸는 불변 객체일 때, 생성자 주입을 한다.
- final 제어자를 써서 불변으로 만드는 것이다.
- 불변 객체는 setter를 만들면 안된다. setter는 값을 바꾸는 것이기 때문에 코드 안정성이 떨어진다.
4. 생성자주입을 하는 객체들은 null을 허용하지 않는다.
- 값을 채워넣어야한다.
- 생성자 주입은 특히 의존성이 애플리케이션의 시작 시점에 반드시 필요한 경우에 적합하다.
5. 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입 된다.
5. 수정자 주입(setter 주입)
- setter 메서드를 통해 의존관계를 주입하는 방법이다.
✅수정자 주입 예시 코드
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired(required = false)
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
⭐특징
1. 변경 가능성이 있는 의존관계에 사용한다.
- 생성자 주입과 달리 final 제어자를 쓰지 않는다.
- 런타임 중에 의존성을 변경할 수 있는 유연성을 제공하지만, 불변성을 보장할 수 없으므로, 오류 발생 가능성이 높아질 수 있다.
2. 주입할 대상이 없으면 오류가 발생한다.
- 주입할 대상이 없어도 동작하게 하려면 `@Autowired(required = false)` 로 지정하면 된다.
6. 생성자 주입 vs 수정자 주입(setter) 비교
생성자 주입이 수정자 주입보다 더 권장되는 이유를 정리해보았다.
생성자 주입 | 수정자 주입(setter) | |
불변성 | 객체가 생성될 때 모든 의존성이 제공되므로, 이후에 객체 상태를 변경할 수 없다. -> 예측 가능하며 버그 발생 가능성이 적다. |
객체 생성 후, 언제든지 상태를 변경할 수 있다. -> 불변성을 보장하지 않으므로, 오류 발생 가능성이 높아질 수 있다. |
의존성 명시 | 필요한 모든 의존성이 생성자를 통해 명시적으로 제공된다. -> 의존성이 명확하게 드러난다. |
setter 메서드가 호출되기 전까지 의존성이 누락될 수 있다. -> 클래스가 사용될 때 의존성이 모두 설정되었는지 자동으로 확인하기 어렵다. |
초기화 안정성 | 객체가 사용되기 전에 완전히 초기화 된다. | 객체가 완전히 초기화되지 않은 상태에서 사용될 위험이 있다. |
테스트 용이성 | 단위 테스트를 작성할 때 생성자를 통해 모의객체Mock이나 Stub을 주입할 수 있다. -> 테스트가 더 단순하고 명확해진다. |
테스트 시 setter 메서드를 각각 호출해야 한다. -> 테스트 코드가 복잡해질 수 있다. |
7. 필드 주입
✅필드 주입 예시 코드
@Component
public class OrderServiceImpl implements OrderService {
@Autowired private MemberRepository memberRepository;
@Autowired private DiscountPolicy discountPolicy;
}
⭐특징
1. 필드 주입은 코드가 간결해서 편하지만 사용하지 말자.
2. 외부에서 변경이 불가능해서 테스트 하기 힘들다
- 캡슐화 유지를 위해 필드를 'private'으로 선언해야한다.
3. 필드 주입은 스프링 컨테이너 없이 의존 객체를 주입해줄 방법이 없다.
- @SpringBootTest으로 스프링 컨테이너를 일일이 띄워서 실행해야한다.(스프링 컨테이너를 테스트에 통합한 경우)
-> 비효율적이다.
4. 순수한 자바 테스트 코드에서는 @Autowired가 동작하지 않는다.
- JUnit은 순수 자바 환경에서 동작한다. JUnit 자체는 스프링에 의존하지 않는다.
- 단순히 new 연산자를 통해 객체를 생성하는 경우, 해당 객체의 의존성들은 자동으로 주입되지 않는다.
-> NullPointerException이 발생된다.
-> 그래서, setter 메소드를 통해 테스트 중에 의존성을 주입할 수 있다.
-> 코드가 복잡해지고, 이럴거면 처음부터 setter 메소드를 쓰는게 낫다.
8. 일반 메서드 주입
- 일반 메서드를 통해 주입할 수 있다.
✅일반 메서드 주입 예시 코드
@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)|롬복과 최신 트랜드 (0) | 2024.05.06 |
---|---|
스프링 핵심 원리 기본편 - 의존관계 자동 주입(2)|옵션처리 (0) | 2024.05.04 |
스프링 핵심원리 기본편 - 컴포넌트 스캔 (0) | 2024.05.01 |
스프링 핵심 원리 기본편 - 싱글톤 패턴과 스프링 컨테이너 (0) | 2024.04.30 |
스프링 핵심 원리 기본편 - AppConfig (0) | 2024.04.29 |