[Spring] Constructor Injection 방식을 권장하는 이유
요즘 아무 생각 없이 의존성 주입을 @Autowired
를 사용한 필드 주입 방식으로 작성하다보니, 인텔리제이에서 경고메시지를 보여줬다.
Field injection is not recommended .. Always use constructor based dependency injection in your beans
또한 백기선님의 스프링 부트 강의를 듣고있었는데
- 단일 생성자이며, 그 생성자의 파라미터가 빈일경우 스프링이 생성자를 자동으로 주입해준다고하였다.
- @Autowired를 사용하지 않아도 된다.
public class Member {
private final ProductService productService;
public Member(ProductService productService) {
this.productService = productService;
}
}
생각해보니 의존성 주입의 방법에는 여러가지 방법이 있었고, 그 중 생성자 주입방식이 제일 좋다는 얘기는 종종 들어보았다.
왜???
의존성 주입의 방법으로는 크게 3가지가 있다.
- 생성자 주입(Constructor Injection)
- 필드 주입(Field Injection)
- 세터 주입(Setter Injection)
나는 생성자 주입 방식에 대해서만 정리하려 한다.
생성자 주입 방식
@Service
public class ConstructorInjectionService {
private final OtherService otherService;
public ConstructorInjectionService(OtherService otherService) {
this.otherService = otherService;
}
public int someMethod() {
return otherService.operation();
}
}
위와 같이 생성자로 의존하는 Bean을 받으면, 스프링에서 정의하는 어노테이션을 사용하지 않을 수 있기 때문에
컨테이너와 독립적으로 클래스가 작성 가능하다.
생성자 주입 방식을 권장하는 이유
-
단일 책임의 원칙
생성자의 인자가 많아지면서 하나의 클래스가 많은 책임을 떠안는다는 걸 알게된다.
그래서 Constructor Injection을 사용해 의존관계, 복잡성을 쉽게 알 수 있다.
-
의존성이 숨는다
DI(Dependency Injection) 컨테이너를 사용한다는 것은 클래스가 자신의 의존성만 책임지는게 아니다.
제공된 의존성 또한 책임진다. 그래서 클래스가 어떤 의존성을 책임지지 않을 때, 메소드나 생성자를 통해
커뮤니케이션이 되어야한다. 하지만 Field Injection은 숨은 의존성만 제공해준다.
-
DI 컨테이너의 결합성과 테스트의 용이성
DI 프레임워크의 핵심은 관리되는 클래스가 DI 컨테이너에 의존성이 없어야
한다.
즉, 필요한 의존성을 전달하면 독립적으로 인스턴스화 할 수 있는 단순 POJO여야한다.
DI 컨테이너 없이 유닛테스트에서 인스턴스화 가능하며, 테스트 가능하다.
컨테이너 결합성이 없다면 관리하거나 관리하지 않는 클래스를 사용할 수 있고, 다른 DI 컨테이너로 전환할 수 있다.
-
Immutability (불변 객체)
생성자 주입 방식에서 필드는 final
로 선언할 수 있다.
하지만, 필드 주입 방식에서는 final로 선언할 수 없어 객체가 변경 가능한 상태
가 된다.
-
순환 의존성
생성자 주입 방식에서 순환 의존성을 가질 경우 BeanCurrentlyCreationExcepiton
을 발생시킴으로써 순환 의존성을 알 수 있다.
1번 클래스가 2번 클래스를 참조하는데, 다시 2번 클래스가 1번 클래스를 참조하는 경우 순환 의존성이라고 부른다.
왜 사용하는지 알고 넘어가자.