의존성을 주입하는 방법은 총 3가지가 있다.
- Field Injection
- Setter based Injection
- Constructor based Injection
사실 생성자 주입이 좋다고는 다들 알고 있을 것이다. 그렇다면 왜 생성자 주입이 좋을까?
(해당 부분을 더 자세히 정리하고자 포스팅한다.)
Field Injection
Field Injection은 의존성을 주입하고자 하는 필드에 @Autowired 애노테이션을 붙이면 된다.
@RestController
public class MemberController {
@Autowired
private MemberService memberService;
}
필드 주입 방식은 아래와 같은 절차로 동작한다.
- 주입받으려는 빈의 생성자를 호출하여 빈을 찾거나 빈 팩토리에 등록
- 생성자 인자에 사용하는 빈을 찾거나 만듬
- 필드에 주입
해당 방식의 문제는 주입할 빈이 없어도 빈 생성이 가능하다는 것이다. 즉, 필드에 의존을 주입하지 않아도 스프링에서 에러가 터지지 않는다.
그래서 앱이 구동된 이후 실제로 memberService에 접근할 때 비로소 NPE가 발생하게 된다.
Setter based Injection
Setter 메서드에 @Autowired 어노테이션을 붙여 의존성을 주입하는 방식이다.
@RestController
public class MemberController {
private MemberService MemberService;
@Autowired
public void setMemberService(MemberService MemberService){
this.MemberService = MemberService;
}
}
필드 주입 방식과 과정이 유사하며, 다른 점은 필드에 바로 주입하는 것이 아니라 주입하려는 빈 객체의 수정자(Setter)를 호출하여 주입을 한다는 점이다.
해당 방식도 필드 주입 방식과 동일한 문제가 있다. 주입할 빈이 없어도 해당 빈이 생성되고 에러 처리가 되지 않는다.
그래서 memberService에 접근할 때 (예제 코드 기준) 비로소 NPE가 발생한다.
Constructor based Injection
생성자를 사용해서 의존성을 주입하는 방식이다.
@RestController
public class MemberController {
private final MemberService MemberService;
public MemberController(MemberService MemberService){
this.MemberService = MemberService;
}
}
해당 방식은 객체가 생성되는 시점에 빈을 주입하기 때문에 주입할 빈이 없다면 에러가 터진다.
즉, 앱 구동 시점에 즉시 에러를 확인할 수 있다.
추가
생성자 주입을 사용하는 가장 큰 이유는 주입할 빈이 없을 때에 대한 에러 처리 부분일 것이다. 하지만 꼭 주입할 빈이 없는 문제만 고려한 것은 아니다.
-잘못된 설계 확인-
예를 들어 아래의 경우를 생각해보자.
- A 클래스의 a 메서드는 B 클래스의 b 메서드를 호출한다.
- B 클래스의 b 메서드는 A 클래스의 a 메서드를 호출한다.
이때 필드 주입 방식을 사용하면 A 빈과 B 빈에 서로의 빈 주입이 모두 정상적으로 완료가 된다!
- 이후 A 빈의 a 메서드 혹은 B 빈의 b 메서드를 호출할 때 순환참조가 발생하므로 StackOverFlowError가 나면서 앱이 꺼진다.
생성자 주입 방식을 사용하면 A 클래스가 B에 의존하고, B 클래스가 A에 의존하면 애초에 빈이 생성되지도 않고 에러를 발생한다.
- 즉, 잘못된 설계를 사전에 방지할 수 있다는 장점이 있다.
(이런 잘못된 설계는 SonarQube나 ArchUnit 등을 사용해서 체크할 수도 있다.)
-Immutable-
추가로 생성자 주입 방식은 final 키워드를 사용해서 불변성(Immutable)의 장점을 살리고 Null이 아님을 보장할 수 있다.
참고
'Server > Spring' 카테고리의 다른 글
Spring - 멀티 모듈을 적용하면서 (+ CQRS, 이벤트 기반 아키텍처 적용) (0) | 2023.01.25 |
---|---|
Spring - Servlet과 Spring의 차이! (0) | 2022.11.16 |
Spring - consumes와 produces 이해하기! (0) | 2022.11.02 |
Spring - 다중 데이터 소스 환경에서 spring.sql.init이 동작하지 않는 이슈 해결 (0) | 2022.10.22 |
Spring - Request DTO에 NoArgsConstructor의 AccessLevel을 어떻게 설정할까?! (3) | 2022.10.03 |