의존관계 주입 방법
- 생성자 주입
- 수정자 주입 (setter주입)
- 필드 주입
- 일반 메서드 주입
생성자 주입
@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;
}
}
@Component, @Autowired
이전 sec6에서 다뤘던 내용이 생성자 주입.
- 특징
- 생성자 호출시점에 딱 1번만 호출되는 것이 보장.
- 불변, 필수
생성자가 딱 1개만 있으면 @Autowired
생략해도 자동으로 주입됨.
빈을 등록하면서 의존관계 주입도 같이 일어남. 왜냐하면 객체를 생성하면 자동으로 생성자가 불려지게 되므로 또 어쩔수없이 의존관계주입도 같이 해줘야만함.
수정자 주입(setter주입)
@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;
}
}
스프링 라이프사이클 : 1.스프링 빈 생성 -> 2.의존관계설정
setXxxx에 @Autowired
붙이면 의존관계 설정됨.
생성자 먼저, setter가 다음으로 의존관계설정됨.
- 특징
- 선택, 변경가능성있는 의존관계에 사용
필드 주입
필드에 바로 주입하는 방식
@Autowired private MemberRepository memberRepository;
- 특징
- 코드가 간결하지만 외부에서 변경불가능해서 테스트가 힘들다는 치명적 단점.
필드인젝션으로 순수자바코드로 테스트하려면 세터를 만들어야 함. 그럴바엔 세터에 @Autowired하는게 나음. 필드인젝션은 안쓰는게 좋음.
세터없이 필드인젝션으로 테스트하려면 스프링 다 띄우고 테스트해야함. -> 순수 자바코드를 사용한 부분적 테스트 진행이 어려움.
스프링없이 순수한 자바코드로 테스트를 진행하려 할 때,
//OrderServiceImple.java
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
//OrderServiceTest.java
@Test
void fieldInjectionTest() {
OrderServiceImpl orderService = new OrderServiceImpl();
//setter를 만들어 memberRepository와 discountPolicy를 넣어줘야 NullPointerException안남.
orderService.setMemberRepository(new MemoryMemberRepository());
orderService.setDiscountPolicy(new FixDiscountPolicy());
orderService.createOrder(1L, "itemA", 10000);
}
Ex)createOrder()
부분 테스트를 위해 스프링을 띄우지 않고 순수 자바코드로만 테스트 하는 경우 - memberRepository
를 임시로 다른걸로 바꿔서 테스트한다했을 때,
필드주입의 경우는 그냥createOrder()
만 하면 memberRepository와 discountPolicy에서 NullPointerException남. 해당하는 값을 넣어줘야하는데 안넣어줬기 때문.
생성자 주입의 경우는 new OrderServiceImpl(memberRepository, discountPolicy)
하면서 값을 넣어주는게 필수가 되지만 필드주입은 그렇지 않음.
따라서 memberRepository, discountPolicy 에 해당하는 값을 넣어줘야 하는데 그렇게 하려면 setter를 이용할 수 밖에 없음.(자바빈 프로퍼티 규약 참고)
따라서 다시 setter를 만들고 setter로 memberRepository값을 넣어줌. 이렇게 되면 수정자(setter)주입과 다를게 별반 없게됨. 오히려 그럴바엔 수정자(setter)주입하는게 나음.
> 참고 : 자바빈 프로퍼티, 자바에서는 과거부터 필드의 값을 직접 변경하지 않고, setXxx, getXxx과 같은 메서드를 통해 값을 읽거나 수정하는 규칙이 있음. -> 자바빈 프로퍼티 규약
일반 메서드 주입
- 일반 메서드를 통해 주입받을 수 있다.
- 특징
- 한번에 여러 필드 주입받을 수 있음.
- 일반적으로 잘 사용하지 않음. (보통 생성자,수정자 주입 안에서 다 해결하기 때문에 메서드 주입 사용하는 경우는 거의 없음.)
> 참고 : 의존관계 자동주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작함. 스프링 빈이 아닌 Member
같은 클래스에서 @Autowired
코드를 적용해도 아무 기능도 동작하지 않음.
> 참고 : @Autowired
의 기본 동작은 주입할 대상이 없으면 오류가 발생. 주입할 대상이 없어도 동작하게 하려면 `@Autowired(required = false)로 지정하면 된다.