스프링이 객체 생성 후 리플렉션으로 필드에 값을 넣는 방식
객체를 만들기 전에 생성자 파라미터로 의존성을 넣어주는 방식
@Component
@RequiredArgsConstructor
public class MyService {
private final UserRepository userRepository;
}
/*lombok이 만든 생성자 */
public MyService(UserRepository userRepository) {
this.userRepository = userRepository;
}
스프링은 해당 코드를 보고 아래의 step으로 생각한다.
-> Myservice를 생성하려면 UserRepository가 필요함
-> 그럼 UserRepository 빈을 찾아서 생성자 파라미터에 넣어야함
즉, 스프링이 객체 생성 시점에 빈을 넣어주는 방식
-> 그래서 final 필드도 가능하고
-> 의존성 없으면 그자리에서 에러가 발생
-> 타입이 일치하는 빈을 스프링 컨테이너에서 찾음
@Component
public class MyUtil {
@Value("${my.prop}")
private String prop;
}
해당 코드는 생성자가 아니라 객체 생성 후에 리플렉션으로 값을 넣는 방식이다.
여기서 스프링 컨테이너 흐름 정리를 하자면
1. 객체 인스턴스 생성(기본 생성자를 호출)
2. 스프링이 리플렉션으로 필드를 찾음
3. 그 위에 @Value가 붙어있으면 yml값을 꽂아 넣어줌
즉, 생성 -> 주입 순서이다.
여기서! 헷갈리는 실수
🧨 왜 @Value + final + @RequiredArgsConstructor 조합이 터지는가?
@Component
@RequiredArgsConstructor
public class MyClass {
@Value("${my.prop}")
private final String prop;
}
/*그러면 롬복은 이렇게 생성자를 만든다.*/
public MyClass(String prop) {
this.prop = prop;
}
그런데 여기서 @Value로 yml의 값을 넣고자 한다면?
스프링은 @Value가 붙어 있는 것을 신경쓰지 않음 -> 즉, 스프링 입장에서는 클래스 객체 생성하려면 String type의 빈이 필요한데 스프링 컨테이너에 String 빈이 없네? -> 에러 터짐
정리하자면 @Value 가 적용되기 전에 @RequiredArgsConstructor가 먼저 적용되어서 필드가 생성자 주입 대상이 되어버린 것이다.
🎯 최종 정리
| 주입 방식 | 시점 | final 가능? | 스프링이 뭘 찾나? |
|---|---|---|---|
| 생성자 주입(빈 주입) | 객체 생성 전 | ✔ 가능 | 스프링 빈 |
| 필드 주입(@Value, field @Autowired) | 객체 생성 후 | ❌ 불가능 | @Value → yml 값@Autowired → 빈 |
🎯 완전 요약
@RequiredArgsConstructor
→ final 필드를 생성자 주입 대상으로 만듦
→ 스프링은 “해당 타입의 빈이 필요하다”고 생각함
@Value
→ 생성자 호출 후 리플렉션으로 주입
→ final 필드에는 값 못 넣음
두 개를 같이 쓰면
→ 스프링이 yml 값이 아니라 “String 빈”을 요구
→ 당연히 없음 → 에러!