1. 생성자 주입 (Constructor Injection) & 필드 주입 (Field Injection) 이란?
- Field Injection (필드 주입)
@Autowired
private UserService userService;
- Constructor Injection (생성자 주입)
private JavaMailSender javaMailSender;
private RedisTemplate<String, String> redisTemplate;
@Autowired
public EmailServiceImpl(JavaMailSender javaMailSender, RedisTemplate<String, String> redisTemplate){
this.javaMailSender = javaMailSender;
this.redisTemplate = redisTemplate;
}
2. 왜 Constructor Injection을 추천하는가?
의존성 주입할 때 생성자 주입을 추천한다.
깔끔한 코드는 필드 주입인 것 같은데 왜 생성자 주입을 추천할까?
-
Single Responsibility Principle Violation (단일 책임의 원칙 위반)
새로운 의존성을 추가하는 것은 매우 쉽다.
여기서 Constructor Injection을 사용하면 다른 Injection 타입에 비해 위기감 같은 걸 느끼게 해준다.
Constructor의 파라미터가 많아짐과 동시에 하나의 클래스가 많은 책임을 떠안는다는 걸 알게된다.
이러한 것들이 리팩토링을 해야한다는 신호가 될 수 있다. -
Dependency Hiding
DI(Dependency Injection) 컨테이너를 사용한다는 것은 클래스가 자신의 의존성만 책임진다는게 아니다.
제공된 의존성 또한 책임진다. 그래서 클래스가 어떤 의존성을 책임지지 않을 때,
메서드나 생성자를 통해(Setter나 Contructor) 확실히 커뮤니케이션이 되어야한다.
하지만 Field Injection은 숨은 의존성만 제공해준다. -
DI 컨테이너의 결합성과 테스트 용이성
DI 프레임워크의 핵심 아이디어는 관리되는 클래스가 DI 컨테이너에 의존성이 없어야 한다.
즉, 필요한 의존성을 전달하면 독립적으로 인스턴스화 할 수 있는 단순 POJO이어야 한다.
DI 컨테이너 없이도 유닛테스트에서 인스턴스화 시킬 수 있고, 각각 나누어서 테스트도 할 수 있다.
컨테이너의 결합성이 없다면 관리하거나 관리하지 않는 클래스를 사용할 수 있고,
다른 DI 컨테이너로 전환할 수 있다.
하지만, Field Injection을 사용하면 필요한 의존성을 가진 클래스를 곧바로 인스턴스화 시킬 수 없다. -
불변성(Immutability)
Constructor Injection과 다르게 Field Injection은 final을 선언할 수 없다.
그래서 객체가 변할 수 있다. -
순환 의존성
Constructor Injection에서 순환 의존성을 가질 경우
BeanCurrentlyCreationExeption을 발생시킴으로써 순환 의존성을 알 수 있다.
참고 : https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/