개발 공부를 하며 들었던 이야기 중 하나는, 필드 주입보다는 생성자 주입을 쓰라는 이야기였다.
심지어 인턴 당시 사수에게는 필드 주입들을 싹 다 생성자 주입으로 바꾸라는 조언도 들었다.
왜 생성자 주입을 써야할까? 필드 주입이 편하지 않을까?
우선 해당 두 방법의 차이를 알아보자.
1. 생성자 주입
public class OrderController {
// final 변수로 선언
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
}
2. 필드 주입
public class OrderController {
@Autowired
private OrderService orderService;
}
우선 코드를 봤을 때 차이점이 무엇이 있을까?
final 변수가 눈에 보인다. 생성자 주입은 생성자를 활용하기 때문에, final 키워드를 붙여주어야한다.
하지만 Autowired는 final을 사용할 수 없다.
왜 그럴까?
필드 주입은 해당 빈이 생성된 이후에 참조를 하기 때문에 final을 쓸 수가 없다.
해당 내용 하나 가지고 이제 제목에 대한 답변들을 할 수 있다.
1. 불변성
생성자로 의존성 주입을 하면 final로 선언할 수 있기 때문에, 런타임에서 의존성을 주입받은 객체가 변할 일이 없어진다.
하지만 필드 주입은 불필요하게 수정의 가능성을 열어두게 된다.
2. 순환 참조 방지
public class School {
@Autowired
private Company company;
public void pay() {
company.do();
}
}
public class Company {
@Autowired
private School school;
public void do() {
school.pay();
}
}
public class SchoolCompany {
@Autowired
private Company company;
@Autowired
private School school;
public void schoolCompanyDo() {
company.do();
school.pay();
}
}
해당 코드를 가진 프로그램을 컴파일해서 구동해도 잘 동작한다.
그러다 런타임에 내가 schoolCompanyDo()를 호출하면? 순환참조로 인해서 서버가 다운된다.
즉 메서드 실행 시점 전까지 순환 참조가 있더라도 알 수가 없다.
그렇다면 생성자 주입은 다를까?
@RequiredArgsConstructor
public class School {
private final Company company;
public void pay() {
company.do();
}
}
@RequiredArgsConstructor
public class Company {
private final School school;
public void do() {
school.pay();
}
}
@RequiredArgsConstructor
public class SchoolCompany {
private final School school;
private final Company company;
public void SchoolCompanyDo() {
company.do();
school.pay();
}
}
아까와 같이 코드를 실행하면, 에러가 바로 발생한다. 왜그럴까?
바로 빈을 주입하는 순서가 다르기 때문이다.
생성자 주입은 생성자의 인자에 사용되는 빈을 찾거나 빈 팩토리를 만든다.
그 후에 찾은 인자 빈으로 주입하려는 빈의 생성자를 호출한다.
즉 빈을 먼저 생성하는 것이 아니라 주입하려는 빈을 먼저 찾는다.
객체를 생성하는 시점(생성자)에 빈을 주입하기 때문에 서로 참조하는 객체가 생성되지 않은 상태에서 해당 빈을 참조하기 때문에 오류가 발생한다.
하지만 필드 주입은 먼저 빈을 생성하고 . 그이후에 주입하려는 빈을 찾아 주입한다.
3. 테스트 코드 작성 용이
생각보다 테스트를 짜면서 많이 겪는 어려움이 있다.
생성자 주입은 그냥 생성자의 인자에 해당 객체를 넣어주면 된다.
하지만 필드 주입을 사용하면?
Mockito 등을 활용해서 목킹한 후에 테스트를 진행해야 한다.
결론은.. 생성자 주입을 우선적으로 사용하자.
'스프링 정리' 카테고리의 다른 글
JPA의 save와 saveAll의 성능차이 (1) | 2023.12.04 |
---|---|
Path Variable vs Query Parameter (0) | 2023.11.19 |
테스트코드에서의 @Transactional (0) | 2023.10.30 |
Spring Batch - @PersistJobDataAfterExecution (0) | 2023.04.04 |
Spring Security & JWT(Json Web Token) 활용 예제 (0) | 2023.03.28 |