-
Spring Dependency Injection의 종류 - Constructor, Setter, FieldSpring 2021. 10. 16. 00:51
Field Dependency Injection(@Autowired)
@AutoWired ❓
- Spring Framework에서 지원하는 Dependency 정의 용도의 Annotation
- Spring 종속적이지만 정밀한 Dependency Injection이 필요한 경우 유용함
- 해당 어노테이션을 사용해 Bean을 등록할 경우 Injection의 대상이 되는 클래스의 형식은 하나여야 한다(하지만 @Qualifier를 이용해 Injection할 Component의 대상을 지정해줄 수 있다)
@Service public class Item { @Autowired private final Pizza pizza; @Autowired private final Burger burger; }
@Autowired
어노테이션을 사용해 객체에 의존성을 주입하는 방식이다. 사용이 매우 간결해서 많이 이용되는 방식이다.하지만 Intellij에서 이 방식을 사용할 경우
Field injection is not recommended
라는 경고 문구가 나타난다.스프링 공식문서에서도 명시적 Wiring 방식보다 정확하지 않고, 예상치 못한 결과를 초래할 수 있으니 사용을 제한하라고 권유한다.
Field Injection을 권장하지 않는 이유
- 외부에서 변경이 불가능하다
- 객체 간 순환 종속성 문제가 발생할 수 있다
- DI 프레임워크가 동작해야 객체가 생성된다
Circular Dependencies - 순환 종속성 문제
*************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | memberServiceImpl defined in file [/examples/.../memberServiceImpl.class] ↑ ↓ | ItemServiceImpl defined in file [/examples/.../ItemServiceImpl.class] └─────┘
Bean의 생성은 잠재적으로, Bean의 의존성이 주입되고 의존성의 의존성이 주입될 때 형성된다.
따라서 종속성 간의 불일치 문제는 늦게 발견될 수 있는데, 이러한 현상이 최초 생성하거나 그 영향을 받는 Bean에게서 나타날 수 있다.
순환 종속성은 클래스 A가 생성자 주입을 통해 클래스 B의 인스턴스를 요구하고, 클래스 B가 생성자 주입을 통해 클래스 A의 인스턴스를 요구할 때 Container가 순환 참조를 탐지하고 예외를 발생시킨다.
Bean이 존재하지 않거나 순환 종속성이 존재할 경우 이를 컨테이너 로드 시간에 탐지한다. 스프링은 Bean이 실제로 생성될 때 속성을 설정하고 종속성을 주입하는데, 잘못 로딩된 컨테이너는 이후 객체를 요청할 때 예외를 발생시킬 수 있으므로 기본적으로 싱글톤 & 사전 인스턴스 전략을 사용하는 이유가 된다. 따라서 실제로 필요하기 전 미리 만들기 위해 약간의 초기 시간과 메모리를 들여 ApplicationContext가 생성될 때 구성의 문제점을 발견할 수 있다.
- 객체 생성 시점에서 순환 종속성이 발생하는 것
- 객체 생성 후 비즈니스 로직 상에서 순환 종속성이 발생하는 것
의 경우, 후자가 실제 개발 시 훨씬 위험한 문제를 초래하기 때문에 Constructor Injection 방식을 사용하자.
Constructor-based Dependency Injection
@Service public class Item { private final Pizza pizza; private final Burger burger; public Item(Pizza pizza, Burger burger) { // ... } } // Lombok을 이용한 생성자 주입 방식 @Service @RequiredArgsConstructor public class Item { private final Pizza pizza; private final Burger burger; // ... }
생성자 주입 방식의 장점
- Container의 생성 시점에 순환 참조 문제를 발견할 수 있다.
final
keyword를 이용해 객체를 immutable하게 만들 수 있다.- 필요한 종속성이
null
이 아닌지 체크할 수 있다. - 항상 완전히 초기화된 상태에서 코드로 반환된다.
- 너무 많은 인자를 가진 생성자는 Code Smell을 유발한다 -> 클래스가 너무 많은 책임을 가지고 있으니 적절한 관심사 분리를 통해 인수를 분리해야 함을 경고한다
Setter-based Dependency Injection
@Service public class Item { private Pizza pizza; private Burger burger; public void setPizza(Pizza pizza) { this.pizza = pizza; } public void setBurger(Burger burger) { this.burger = burger; } // ... } // Lombok을 이용한 세터 주입 방식 @Service @Setter public class Item { private Pizza pizza; private Burger burger; // ... }
- 인수가 없는 생성자를 호출 or 팩토리 메서드를 호출한 후
setter
메서드를 호출하는 컨테이너에 의해 수행 - 필수 종속성을 생성자 주입 방식으로 의존성을 주입받고, 선택적 종속성을 세터 주입 방식으로 의존성을 주입받는 것을 권장한다.
- 합리적인 기본값을 할당할 수 있는 선택적 종속성에만 사용해야 한다.
- 그렇지 않은 경우 코드가 해당 의존성을 사용하는 모든 곳에서
null
검사를 수행해야 한다
- 그렇지 않은 경우 코드가 해당 의존성을 사용하는 모든 곳에서
참고자료
'Spring' 카테고리의 다른 글
(kotlin)gradle spring boot application dockerize (0) 2023.09.28 STOMP와 WebSocket으로 아주 간단한 메시징 시스템 만들기 (422) 2021.10.19 Spring에서는 DI를 통해 IoC를 구현한다 (416) 2021.10.16 Spring AOP(Aspect-Oriented Programming)의 이해 (407) 2021.10.03 @(Annotation)을 이용한 Spring Container Configuration (449) 2021.10.02