Spring 기반의 웹 애플리케이션에서 컨트롤러 클래스에 싱글톤 객체를 필드에 선언했을 때, 서버 기동 중 빈 초기화 단계에서 NullPointerException이 발생하는 문제를 해결하는 방법을 공유하려고 합니다.
아래와 같은 코드에서 문제가 발생합니다:
@RestController
public class TestController {
private final Tests tests = Tests.getInstance(); // 여기서 예외 발생
...
}
Spring 컨테이너는 컨트롤러를 빈으로 등록하면서 필드 초기화를 수행합니다. 이 시점에 Tests.getInstance() 내부에서 사용하는 DAO가 아직 초기화되지 않아 NullPointerException이 발생하게 됩니다.
Caused by: java.lang.NullPointerException: Cannot invoke "TestDao.select(String)" because the return value of "TestDao.getInstance()" is null
at Tests.init(Tests.java:28)
at Tests.<init>(Tests.java:14)
at Tests.getInstance(Tests.java:35)
at TestController.<init>(TestController.java:27)
이 문제를 해결하기 위해 필드에서 직접 초기화하지 않고, 필요할 때만 메서드 내부에서 호출하도록 변경했습니다.
@RestController
public class TestController {
// 원래 호출 했던 부분
// private final Tests tests = Tests.getInstance();
@RequestMapping("/test/api")
public TestResponse doTest(HttpServletRequest req) {
Tests tests = Tests.getInstance(); // 메서드 내부 호출: 이 시점에는 모든 객체가 정상 초기화된 상태
...
}
}
@Service, @Component 등을 사용한 Spring Bean 주입 방식(@Autowired)으로도 문제를 해결할 수 있지만, 이 경우 기존 구조의 변경이 불가피합니다.
반면, 메서드 내부에서 호출하는 Lazy 패턴은 코드 변경 범위를 최소화하면서도 싱글톤 객체를 안정적으로 사용할 수 있는 간단하고 효과적인 방법입니다. 기존 코드의 변경이 어려운 상황이라면 Lazy 패턴을 추천드립니다.
감사합니다.