@Autowired
가 붙은 필드에 대해 “필드 주입은 권장되지 않습니다(Field injection is not recommended)”
라는 문구를 종종 목격할 수 있음.의존성 주입
은 객체가 직접 의존 객체를 정의하거나 만들지 않고 사용하는 방식.생성자 주입(Constructor injection)
세터 주입(Setter injection)
필드 주입(Field injection)
필드 주입
은 @Autowired
를 사용하여 클래스 내부의 필드에 직접 주입하는 방식이며, 가장 간단하지만 여러 가지 문제점이 있음.DI(Dependency Injection)
옵션으로 제공하지 않음. NullPointerException
이 발생할 수 있는 위험이 있음.Ex) EmailService 클래스에 EmailValidator를 필드 주입.
@Service
public class EmailService {
@Autowired
private EmailValidator emailValidator;
}
public void process(String email) {
if (!emailValidator.isValid(email)) {
throw new IllegalArgumentException("INVALID_EMAIL");
}
}
↑
EmailService는 EmailValidator가 주입되어야만 제대로 동작함.EmailService emailService = new EmailService();
emailService.process("test@naver.com");
↑
위 코드처럼 직접 객체를 생성해버리면 의존성이 주입되지 않기 때문에 NullPointerException
이 발생함.private final EmailValidator emailValidator;
public EmailService(EmailValidator emailValidator) {
this.emailValidator = emailValidator;
}
↑
이렇게 생성자를 활용해서 주입하면 객체 생성 시 반드시 의존성을 제공해야 하므로, NullPointerException
의 위험을 줄일 수 있음.필드 주입을 사용하면 불변 객체(immutable object)를 만들 수 없음.
final
필드는 선언 시 또는 생성자를 통해 초기화 해야하는데final
필드에 주입할 수 없음.의존성이 변경 가능, 가변적(mutable)이기 때문에, 초기화된 후에 변경되지 않을 것이라는 보장이 없음.
권장 방식.
필드 주입
을 사용하면 필요 이상으로 많은 의존성들을 쉽게 추가할 수 있어서 클래스가 여러 역할을 하게 될 가능성이 있음.생성자 주입
을 사용할 경우 생성자 파라미터 수가 많아지면 IDE에서 설계에 문제가 있음이라는 경고를 주므로, 의존성이 과도하다는 신호를 받을 수 있음.@Component
public class DependencyA {
@Autowired
private DependencyB dependencyB;
}
@Component
public class DependencyB {
@Autowired
private DependencyA dependencyA;
}
필드 주입은 의존성이 필요할 때 주입되기 때문에, 스프링(Spring)은 BeanCurrentlyInCreationException
을 발생시키지 않음.
또한 코드에 순환 의존성
이 있다면 이는 설계에 문제가 있다
는 신호일 수 있음.
※
스프링 부트 2.6 버전부터는 순환 의존성이 기본적으로 허용되지 않음.
EmailValidator validator = Mockito.mock(EmailValidator.class);
EmailService emailService = new EmailService();
@Mock
private EmailValidator emailValidator;
@InjectMocks
private EmailService emailService;
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
}
private EmailValidator emailValidator;
private EmailService emailService;
@BeforeEach
public void setup() {
this.emailValidator = Mockito.mock(EmailValidator.class);
this.emailService = new EmailService(emailValidator);
}