2021. 7. 1. 14:23ㆍ프로그래밍/web
스프링을 사용하면서 DI를 사용하는 방법에는 크게 4가지가 있는데 각각의 장단점과 사용 방법에 대해서 알아보자.
1. 생성자 주입
의존성 주입을 생성자에서 수행하는 방법.
생성자는 일반적으로 객체가 생성될때 딱 한번 실행되는 것이 보장된다.
불변 / 필수 의존관계 설정에 사용된다.
@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;
}
}
memberRepository와 discountPolicy를 final로 선언해서, 객체가 생성될때 딱 한 번만 설정되고 뒤에 변경이 없음을 확인할 수 있다.
위의 예제에서는 memberRepository와 discountPolicy가 항상 null이 아님을 확인 할 수있다.
추가로 스프링이 관리하는 container일 경우, 생성자가 한개인 경우에는 @Autowired 어노테이션을 붙이지않아도 자동으로 으로 등록해준다.
2. setter 주입
setter함수를 따로 만들어서 의존관계 주입을 수행하는 방법.
변경 가능성이 존재하는 의존관계에서 사용한다.
@Component
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
}
추후에 OrderService.setMemberRepository(memberRepository)함수를 통해 의존성 주입 객체를 변경할 수도 있으나, 권장되는 방법은 아니다.
3. 필드 주입
field 자체에 DI를 적용하는 방법.
주로 테스트 코드에서나,외부에서 변경이 어려워서, 추가로 setter를 만들어줘야한다는 단점이 존재한다.
@Component
public class OrderServiceImpl implements OrderService{
@Autowired private final MemberRepository memberRepository;
@Autowired private final DiscountPolicy discountPolicy ;
}
자동으로 component scan해서 bean을 등록하는 spring container가 아닌, 아닌 순수한 자바 코드의 경우 테스트 할 수 있는 방법이 없다. NullpointerException이 발생한다.
나중에 다른 코드에서 사용할 일이 없는 TEST코드에서나 configuration에서 간편하게 사용된다.
4. 일반 함수 주입
아무 함수 위에 @Autowired를 붙이면 해당 함수에서 생성자 주입이 가능하다.
한번에 여러 필드를 의존성 주입할 수 있는 장점이 있지만, 생성자 주입과 setter를 주로 사용하고, 일반 함수 주입은 주로 사용하지 않는다.
@Component
public class OrderServiceImpl implements OrderService{
private MemberRepository memberRepository;
@Autowired
public void init(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
}
생성자 주입이 권장되는 이유
생성자 주입을 사용하면, 프로그래머의 실수를 크게 줄일 수 있다.
1. 의존 관계들을 누락하는 경우를 막을 수있다.
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy ;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
// 생성자 주입의 경우 컴파일 에러 발생
// setter 주입인 경우 컴파일 에러를 발생시키지 않지만 돌려보면 에러 발생
OrderServiceImpl orderService = new OrderServiceImpl();
위와 같이 작성한 경우 setter 주입의 경우 컴파일 에러가 발생하지 않아, 직관적이지 못하다.
2. final을 통해, 초기화 단계에서 dependency를 할당하지 않는 문제를 예방할 수도있다.
@Component
public class OrderServiceImpl implements OrderService{
// final은 생성자에서나, 초기 지정으로만 할당이 가능하다
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy ;
// 에러 발생
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
}
}
프로그래머가 실수로 생성자에 di를 하지 않는 경우가 발생할 수 도있는데, 이런 경우 final로 선언이 되어있으면 컴파일 에러가 발생한다.
결론
생성자 주입은 프로그래머의 실수가 발생한 경우 compile 에러를 발생시킨다.
compile 에러는 짱이다.
생성자 주입 짱짱
'프로그래밍 > web' 카테고리의 다른 글
[JAVA] abstract class vs interface (0) | 2021.08.02 |
---|---|
[Spring] Spring MVC framework를 사용한 웹은 어떻게 작동할까? (0) | 2021.07.19 |
[Spring boot] 싱글톤 패턴의 쓰래드 문제 (0) | 2021.06.05 |
REST API의 규칙 (0) | 2021.05.13 |
[Spring boot]DataSource, Repository, Service, Domain, Controller, bean 이란? (0) | 2021.05.02 |