[23/04/19] DI의 4가지 방법
갓영한의 "스프링 핵심원리 기본편 - 다양한 의존관계 주입 방법"에서 참고
공통 가정
- 미관상 인터페이스 MemberRepository, DiscountPolicy, OrderService 생략
- 미관상 구현클래스 MemoryMemberRepository, RateDiscountPolicy 생략
- 미관상 구성정보클래스 AutoAppConfig 생략
1. 생성자 주입
: 파라미터로 들어온 객체들이 모두 컨테이너에 Spring Bean으로 등록되어 있다고 가정
@Component // Spring Bean으로 등록할 것
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired // Component간의 자동의존관계주입, 생성자가 하나라면 @Autowired 생략 가능
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) { // DIP, 의존관계 역전
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
특징
1. OrderServiceImpl 클래스를 bean 객체로 컨테이너에 등록할 때 생성자를 단 1번만 호출 -> 의존관계 불변
2. OrderServiceImpl 클래스를 bean 객체로 컨테이너에 등록하기 위해선 멤버들을 꼭 초기화해야 함 -> 의존관계 필수
2. 수정자 주입
: 파라미터로 들어온 객체들이 모두 컨테이너에 Spring Bean으로 등록되어 있다고 가정
@Component // Spring Bean으로 등록할 것
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository; // setter 사용은 변수를 final로 사용하지 않겠다는 의미
private DiscountPolicy discountPolicy; // setter 사용은 변수를 final로 사용하지 않겠다는 의미
@Autowired // Component간의 자동의존관계주입
public setMemberRepository(MemberRepository memberRepository) { // DIP, 의존관계 역전
this.memberRepository = memberRepository;
}
@Autowired // Component간의 자동의존관계주입
public setDiscountPolicy(DiscoutnPolicy discountPolicy) { // DIP, 의존관계 역전
this.discountPolicy = discountPolicy;
}
}
특징
1. OrderServiceImpl 클래스를 bean 객체로 컨테이너에 등록한 이후에 의존관계 변경가능 -> 의존관계 선택, 변경
2. 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방식
3. 만약 다른 bean 객체와의 의존관계를 선택, 변경할 때 해당 bean 객체가 컨테이너에 존재하지 않다면 오류가 발생할 수 있음. 주입할 대상이 없어도 동작하게 하려면 @Autowired(required = false)로 지정
3. 필드 주입
: 필드에 주입될 Spring Bean들이 모두 컨테이너에 등록되어 있다고 가정
@Component // Spring Bean으로 등록할 것
public class OrderServiceImpl implements OrderService{
@Autowired private MemberRepository memberRepository; // Component간의 자동의존관계주입
@Autowired private DiscountPolicy discountPolicy; // Component간의 자동의존관계주입
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) { // DIP, 의존관계 역전
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
특징
1. 생성자, 수정자 메서드, 일반 메서드가 존재하지 않기 때문에, 필드들이 private 이기 때문에 일단 한번 컨테이너 속의 bean 객체가 필드에 주입된 이후(의존관계가 형성된 이후)에는 다시 의존관계를 선택, 변경할 수 없다
2. DI 프레임워크(스프링)이 있기 때문에 그나마 필드 의존관계주입이라도 가능한 것이다. DI 프레임워크가 없었다면 필드 의존관계 주입도 불가능했을 것이다.
3. 필드 주입은 사용하면 안된다! 사용하려면 어플리케이션 실제 코드와는 관계 없는 테스트 코드나 구성정보 클래스에서만 특별한 용도로 사용하자
4. 필드 주입으로 Spring Bean 간 의존관계는 형성됐을지 몰라도 인스턴스의 멤버들이 초기화되는 것은 아니다! 만약 위 코드 기준으로 new OrderServiceImpl() 으로 orderServiceImpl 객체를 만든다면 NullPointerException이 발생할 것이다. 왜냐하면 필드 주입으로 Spring Bean 간 의존관계는 형성되었을지는 몰라도 OrderServiceImpl 객체의 멤버는 초기화된 것은 아니기 때문이다. 그래서 필드 주입은 혼자서 사용하는 것이 아니라 생성자, 수정자 메서드, 일반 메서드를 같이 사용해야 한다.
5. 결국 그럼 필드 주입 쓸 바에 생성자, 수정자 메서드, 일반 메서드로 의존관계 주입하는 것이 더 나은 방법이 된다.
4. 일반 메서드 주입
: 파라미터로 들어온 객체들이 모두 컨테이너에 Spring Bean으로 등록되어 있다고 가정
@Component // Spring Bean으로 등록할 것
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired // Component간의 자동의존관계주입
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
특징
1. 한번에 여러 필드를 주입받을 수 있다.
2. 일반적으로 잘 사용하지 않는다. 이미 생성자 의존관계라는 좋은 의존관계 주입 방법이 있기 때문