의존관계 주입(DI)
의존관계 주입!! 이해라도 해보자!!!!
의존관계 주입 방법을 나누면 크게 4가지가 있다.
● 생성자 주입
● 수정자 주입(setter 주입)
● 필드 주입
● 일반 메서드 주입
생성자 주입
생성자를 통해서 의존 관계를 주입 받는 방법
특징
1.생성자 호출시점에 1번만 호출되는 것이 보장된다.
2.불변,필수 의존관계에 사용
@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;
}
}
※ 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입된다. (스프링 빈에만 해당)
수정자 주입(setter 주입)
setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해 의존관계를 주입하는 방법
특징
1.선택, 변경 가능성이 있는 의존관계에 사용
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
※ @Autowired 의 기본 동작은 주입할 대상이 없으면 오류 발생, 주입할 대상이 없어도 동작하게 하려면
@Autowired(required = false) 로 지정해 주어야 함
필드 주입
필드에 바로 주입하는 방법
특징
1.코드가 간결해 보이지만 외부에서 번경이 불가능해서 테스트하기 힘듦
2.DI 프레임워크가 없으면 아무것도 할 수 없다.
3.사용하지 않는 것이 좋음
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
```
※ @SpringBootTest 처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능하다.
메서드 주입
일반 메서드를 통해서 주입 받는 방법
특징
1.한번에 여러 필드를 주입 받을 수 있음,
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;
}
}
```
※ 의존관계 자동주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다. 스프링 빈이 아닌 클래스에서는 @Autowired 코드를 적용해도 아무 기능도 동작하지 않는다.
생성자 주입을 사용하자!!!
과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입
을 권장한다.
생성자 주입이 좋은 이유
1.불변
● 대부분의 의존관계 주입은 한번 실행되면 변경할 일이 없다(오히려 변하면 안된다)
●수정자 주입을 사용하면, set 메서드를 public으로 열어 두여야한다.(변경하면 안되는 메서드를 열어두는 것은 좋지 않은 설계 방법이다.)
●생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다.
2.누락
수정자 의존관계에서 자바 코드를 단위 테스트 하는 경우
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
//...
}
※ @Autowired 가 프레임워크 안에서 동작할 때는 의존관계가 없으면 오류가 발생하지만, 지금은 프레임워크 없
이 순수한 자바 코드로만 단위 테스트를 수행하고 있다.
@Test
void createOrder() {
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.createOrder(1L, "itemA", 10000);
}
이렇게 테스트를 하면 실행은 되지만 NPE(Null Point Exception) 이 발생 하는데 memberRepository, discountPolicy 모두
의존관계 주입이 누락되었기 때문이다.
생성자 주입을 사용하면 다음처럼 주입 데이터를 누락 했을 때 컴파일 오류가 발생한다.
3.final 키워드
생성자 주입을 사용하면 필드에 `final` 키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 값이 설정되지 않는 오
류를 컴파일 시점에 막아준다.
※ 수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되므로, 필드에 `final` 키워드를 사용
할 수 없다. 오직 생성자 주입 방식만 `final` 키워드를 사용할 수 있다.