반응형
의존관계 주입 DI
의존관계 주입이란 스프링 컨테이너가 직접 빈 관리를 하며 의존 관계를 주입해주는 것을 의미한다. 의존관계 주입은 크게 4가지 방법이 있다.
- 생성자 주입(가장 추천하는 방식)
- 수정자 주입(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 가 있으면 스프링 컨테이너에서 생성자의 매개변수에 속해 있는 것들을 꺼내와서 주입해준다.
- 생성자 호출 시점에 딱 1번만 호출하는 것을 보장
- 불변, 필수 의존관계에 사용
- setter 메서드를 만들지 않고 final 키워드를 통해 추후 다른사람이 외부에서 변경하여 버그가 발생하지 않도록 방지
- 생성자가 하나일 경우엔 @Autowired 생략이 가능하다.
수정자 주입 (setter)
@Component
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
- final 키워드를 지우고 생성자 대신 setter를 이용해 주입
- @Autowired를 안해주면 자동 주입이 안된다. (오류 발생)
- @Autowired(required=false)로 지정시 주입할 대상이 없어도 동작하게 실행은 할 수있다.
- 선택, 변경 가능성이 있는 의존관계 사용하지만 이런 경우는 드물다.
필드 주입
@Component
public class OrderServiceImpl implements OrderService{
@Autowired private MemberRepository memberRepository;
@Autowired private DiscountPolicy discountPolicy;
@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);
}
}
- 의존관계를 필드에 바로 주입
- DI 프레임워크 없이 순수 자바코드로 아무것도 할 수 없다.
- 실제 애플리케이션 코드와 관계없는 테스트 단계안에서 사용한다. (@SpringBootTest)
@Test
void fieldInjectionTest() {
OrderServiceImpl orderService = new OrderServiceImpl();
// 값을 넣어 줄 수 없으므로 NullPointException 발생
orderService.createOrder(1L, "itemA", 10000);
}
- 코드가 간결하지만 외부에서 변경이 불가능해서 테스트하기 어려운 단점이 생겨 권장하지 않는다.
- setter를 열어서 주입받을 의존 관계를 연결해줘야 테스트 상에서 사용할 수 있다.
- 실제 클래스에서는 내부적으로 필드 주입을 받았기 때문에 사용가능하지만, 테스트 코드 상에선 주입받은 것을 가져오기가 어렵다. 즉, 사용하기엔 정말 편하지만 테스트에 적합하지 않아 안티패턴으로 볼 수 있다.
일반 메서드 주입
@Component
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
- 일반 메서드에 의존 관계 주입
- 한번에 여러 필드를 주입 받을 수 있다.
- 생성자 , 수정자 주입 안에서 의존 관계를 해결하기 때문에 사용하지 않는다.
옵션 처리
@Test
void AutowiredOption() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);
}
static class TestBean {
@Autowired(required = false)
//스프링에 관리되는 빈이 아예 없는 상황
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = " + noBean1);
}
@Autowired
//스프링에 관리되는 빈이 아예 없는 상황
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
@Autowired
public void setNoBean3(Optional<Member> noBean3){
System.out.println("noBean3 = " + noBean3);
}
}
/*
noBean1 = 호출 자체가 안된다.
noBean2 = null
noBean3 = Optional.empty
*/
@Autowired(required=false)
: 자동 주입할 대상이 없으면 메서드 자체가 호출이 안됨@Nullable
: 자동 주입할 대상이 없으면 null 입력Optional<>
: 자동 주입할 대상이 없으면 Optional.empty 입력
생성자 주입을 선택하는 이유
- 불변 : 대부분 애플리케이션은 종료시점까지 의존관계를 변경할 일이 없다.
- 수정자 주입을 하려면 setXXX 메서드를 public으로 열어두어야 한다.
- 누군가 실수로 변경할 수 있기 때문에 좋은 설계가 아니다.
- 객체 생성할 때 한번만 호출 되므로 불변하게 설계할 수 있다.
- final 키워드를 이용해 생성자를 통해서만 값을 지정함으로 변경을 자동 방지해준다.
- 가끔 필수값이 아닌 옵션이 발생하면 수정자 주입을 권장한다. 필드 주입 사용 X
생성자 주입은 객체를 생성할 때마다 딱 1번만 호출되는 것을 호출할 수 있어서 불변하게 설계할 수 있다. 또한 세터 주입을 봐보면 순수 자바코드로만 테스트할 때 컴파일 시점에 오류가 없다. 그 이유는 컴파일 시점에서 의존 관계 주입의 누락 여부를 확인하지 않기 때문이다. 하지만 생성자 주입을 사용하면 주입 데이터를 누락 했을 때 컴파일 오류가 발생하여서 어떤 값을 주입해야 하는지 알 수 있다. 필수 값이 아닌 경우에만 수정자 주입을 사용하고 필드 주입은 사용하지 말자.
본 포스팅은 인프런 김영한님 강의(스프링 핵심원리 - 기본편)를 토대로 정리한 내용입니다.
반응형