의존관계주입의 종류
의존관계주입의 종류는 크게 4가지가 있습니다.
지금까지 예제에서 가장많이 사용해온 생성자 주입은 생성자를 통해서 의존관계를 주입하는 것입니다. 생성자 호출시점에 주입되기 때문에 한번만 주입되는 것을 보장하고 불변, 필수 의존관계에 사용하면 좋습니다.
@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;
}
}
이때 생성자가 하나면 @Autowired생략가능합니다.
수정자 주입은 setter를 통해서 의존관계를 주입하는 방법입니다. 선택과 변경의 가능성이 있는 의존관계에 사용하면 좋습니다. 이는 직접 필드의 값을 변경하지않고 get~, set~의 메서드를 이용하는 자바빈 프로퍼티의 규약을 따르는 방식입니다.
@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라고 붙여주면 됩니다. 정말 간단하지만 이 방법은 💣 치명적은 단점 💣 이 있습니다. 바로 외부에서 변경이 불가능합니다.
앞에 생성자나, 수정자 주입방법은 테스트 코드를 작성할 때 스프링을 돌리지 않고도 자바코드만으로 의존성을 주입해줄 수 있습니다. 하지만 필드주입은 스프링같이 DI프레임워크가 없으면 주입할 수 있는 방법이 없기 때문에 유연성이 떨어집니다. 그래서 테스트코드안에서 간단히 의존성 주입을 할 때 사용되며 실제 개발에는 권장되지 않습니다.
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
수정자 주입의 일반적인 케이스라고 생각하면 되는 방법입니다. 장점으로는 한번에 여러 필드에 의존성을 주입할 수 있다는 것이있습니다. 하지만 일반적으로 사용되는 방법은 아닙니다. 주로 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;
}
}
의존성 관계 주입의 옵션
주입할 스프링 빈이 없으면 일반적으로는 오류가 발생합니다. 이때 주입할 스프링 빈이 없어도 오류가 나지 않고 돌아가게 하는 방법은 3가지가 있습니다.
먼저 Autowired에 required값을 false로 만들어주는 것입니다. 지금의 예제에서는 Merber타입의 빈이 없다고 인식하고 false옵션을 보고 setNoBean1은 더이상 호출되지 않습니다. 따라서 출력문은 없습니다.
//호출 안됨
@Autowired(required = false)
public void setNoBean1(Member member) {
System.out.println("setNoBean1 = " + member);
}
두 번째 방법은 파라미터 앞에 @Nullable을 붙여주는 것입니다. 이렇게하면 null이 반환됩니다.
//null 호출
@Autowired
public void setNoBean2(@Nullable Member member) {
System.out.println("setNoBean2 = " + member);
}
마지막으로 Optional로 파라미터 타입을 묶어줍니다. 이렇게하면 Optional.empty가 출력됩니다.
@Autowired(required = false)
public void setNoBean3(Optional<Member> member) {
System.out.println("setNoBean3 = " + member);
}
결론은 생성자주입!
지금까지 여러가지 방법의 의존성 주입을 알아보았습니다. 대부분의 의존관계 주입은 한번 일어나면 애플리케이션이 종료할때까지 변경될일이 거의 없습니다. 따라서 수정자주입 같은 경우에는 set~메서드를 public으로 열어둬서 중간에 실수로 외부에서 바뀔 가능성이 있기 때문에 좋은 방법은 아닙니다. 이런면에서 생성자 주입이 좋습니다.
또한 생성자주입은 순수자바코드로 테스트를 작성할 때 누락된 파라미터가 있다면 컴파일에러가 납니다. 마지막으로 생성자주입을 하면 필드에 final 키워드를 붙일 수 있어서 생성자 주입이 일어나지 않으면 컴파일 에러를 내줘서 디버깅이 쉽습니다.