티스토리 뷰
[SpringBoot] @Repository와 같은 빈이 생성되는 클래스에서 @Value로 주입받은 변수를 생성자에서 써도 될까?
개발기록 :) 2023. 10. 19. 16:54간단한 Voucher 관리 프로그램을 만들던 도중,
@Value로 주입받은 값을 생성자에서 사용하려니, NPE가 발생하여
한참의 삽질 끝에 정확하게 언제 주입이 되는지 알아보게 되었습니다🚀
상황
바우처 정보를 파일을 통해 관리하기 위해, yaml 파일에 파일 위치를 명시한 후
레포지토리 클래스에서 파일에 접근하여 바우처 정보를 읽고 쓰고자 아래와 같이 코드를 작성하였습니다.
문제의 코드🤔
@Repository
@Profile("default")
public class FileVoucherRepository implements VoucherRepository {
private Map<UUID, Voucher> vouchers;
private final File voucherInfoCsv;
@Value("${spring.file.voucher.path}")
private String vouchInfoCsvPath;
private final static Logger logger = LoggerFactory.getLogger(FileVoucherRepository.class);
public FileVoucherRepository() {
voucherInfoCsv = new File(vouchInfoCsvPath);
prepareCsv();
vouchers = getAllVouchersFromCSV();
}
// ...
인텔리제이에서 이렇게 값이 표시돼서 되는 줄 알았는데
실행시켜보니, 아래와 같이 vouchInfoCsvPath에 null이 들어가고 있었습니다..🤨
디버깅 결과👾
왜 NPE가발생했을까❓
Spring Framework에서 '@Value'로 주입받은 값은 빈 생성자보다 먼저 주입되지 않는다고 합니다. 이는 스프링의 빈 라이프사이클과 빈 프로퍼티 주입 시기와 관련이 있습니다.
빈 생성의 경우,
@Repository 어노테이션을 사용하여 해당 클래스를 Spring의 빈으로 등록했을 때, 해당 빈의 생성자가 호출됩니다.
하지만 이 시점에서는 아직 '@Value'로 주입받은 프로퍼티가 설정되기 전이므로 해당 프로퍼티는 초기화되지 않는 null 값을 가지게 됩니다.
➡️다시 한번 정리하면, 생성자가 호출이 @Value로 프로퍼티 주입보다 먼저 이루어져 생성자 호출에서 해당 값에 null이 들어가 NPE가 발생하게 된 것입니다.
Spring Bean Life Cycle🚴♀️
간단히 하나씩 살펴보면🔍
1. Instantiation 인스턴스화
빈 객체가 메모리에 생성됩니다. 객체가 생성되고 빈의 클래스의 생성자가 호출됩니다.
2. Populating Properties 프로퍼티 설정
빈의 프로퍼티, 의존성 주입 등이 설정됩니다. @Autowired, @Value, XML 구성 등을 통해 프로퍼티가 주입됩니다.
3. Initialization 초기화
InitializingBean 인터페이스의 afterPropertiesSet() 메서드 또는 @PostConstruct 어노테이션이 적용된 메서드가 호출되어 빈의 초기화 작업을 수행합니다. 이 초기화 메서드를 통해 빈을 사용할 준비를 마칩니다.
4. Destroy 소멸
빈이 더 이상 필요하지 않을 때, 빈의 소멸 작업을 수행합니다. 이 단계에서 DisposableBean 인터페이스의 destroy() 메서드나 @PreDestory 어노테이션이 적용된 메서드가 호출됩니다. 이 단계는 빈의 수명 주기가 끝날 때 발생하며, 메모리 리소스를 해제하거나 다른 정리 작업을 수행하는 데 사용됩니다.
그럼 어떻게 사용할 수 있을까❓
@Value로 주입된 프로퍼티는 빈 생성자가 호출된 후에 설정되므로
빈 초기화 단계에서 호출되는 @PostConstruct 어노테이션이 적용된 메서드를 활용하면 @Value로 주입된 프로퍼티를 안전하게 사용할 수 있습니다.
'백엔드 > SpringBoot' 카테고리의 다른 글
Fetch Join과 비동기로 283배 시간 단축하기! (0) | 2024.01.21 |
---|---|
LazyInitializationException 왜 발생하고, 어떻게 해결할까🧐 (0) | 2024.01.06 |
@Transactional 어노테이션을 뜯어보자. (+ 테스트 시 주의할 점) (0) | 2023.12.11 |
프레임워크를 사용하는 이유는 무엇일까? (0) | 2023.10.13 |
[SpringBoot] 프로젝트 생성하는 다양한 방법 (0) | 2023.10.09 |