앞서 prototype 빈을 사용하는 목적중의 하나로 스프링의 빈이 되면 스프링의 편의 기능을 사용할 수 있다고 했는데 위와 같은 초기화 메서드 지원도 그 예이다. 이때 주의 사항은 prototype 빈은 객체 생성, 의존성 주입 후 초기화 메서드까지만 container가 관리한다는 점이다. 따라서 소멸 메서드는 직접 호출해 줘야 한다.
우리 CoffeeShop은 오픈하긴 했는데 아직 인지도가 없어서 손님이 없는 상황이다. 이때 Barista를 미리 고용해 놓고 있다면 인건비만 나갈 뿐이다. 처음 손님이 커피를 주문할 때 Barista를 고용하면 어떨까?
이처럼 초기화의 시점을 최초 애플리케이션이 실행할 때가 아니라 그빈이 필요할 때 처리하는 것을 게으른 초기화라고 하고 이를 위해@Lazy를 사용한다. 게으른 초기화를 사용하면리소스를 효율적으로 관리하고 애플리케이션의 시작 시간을 단축시킬 수 있다.
@Component@Scope("singleton")@RequiredArgsConstructor@Lazy// 이제 Barista가 필요한 시점에 빈을 생성한다.@Slf4jpublicclassBarista{
privatefinal ApplicationContext ctx;
@PostConstructpublicvoidinit(){
log.debug("바리스타 준비 완료!");
}
}
하지만 지금은 Barista를 게으르게 만들어도 부지런하게 동작한다. 왜냐하면 CoffeeShop이 Barista를 의존성으로 갖고 있고 결국 CoffeeShop이 만들어질 때부터 Barista가 필요한 상황이 되어버린다.
이런 경우에는 ObjectProvider<T>라는 interface를 사용할 수 있다. ObjectProvider<T>는 특정 타입의 빈을 지연 방식으로 검색하고 주입받는다.
@Component@Data@Scope("singleton")publicclassCoffeeShop{
privatefinal ObjectProvider<Barista> baristaProvider; // 객체가 없을 수도 있다.// do somethingpublic Coffee orderCoffee(String name){
return baristaProvider.getObject().makeCoffee(name); // provider를 통해 Baristar를 가져온다.
}
}
오늘 장사 시작: 집기 준비, 재료 준
// 다른 로그들
오늘 장사 마감: 정리하고 정산하고~
게으른 초기화는 얼핏 효율적으로 보이지만너무 무거운 빈을 애플리케이션 동작 중에 초기화 시킨다면 오히려 성능에 문제가 될 수도 있다. 역시 상황에 따라 사용해야 한다.
애플리케이션 라이프사이클
애플리케이션 실행 후 처리
간혹 애플리케이션 시작 시점에 특정 작업을 수행 해야 할 때가 있다. 예를 들어 빈들의 상태를 체크하거나 빈들이 동작하기 전에 배치작업을 수행하는 경우를 생각해볼 수 있다. 이를 위해 모든 빈들이 만들어지고 아직 서비스하기 전에 동작하고 싶다면 해당 코드를 어디다 작성해야 할까?
쉽게 떠오르는 부분은 프로그램의 출발점인 main 메서드이다. 그런데 main 메서드는 static 영역이기 때문에 생성된 빈을 주입받아서 사용하는 등의 작업이 불가능하다.
이런 경우 ApplicationRunner나 CommandLineRunner 인터페이스를 활용할 수 있다. 이 interface들의 콜백은 모든 스프링 빈들이 로드된 후에 동작하는데 실행시 main 메서드로 전달된 argument를 받아서 활용할 수도 있다.
option arguments: 일반적으로 -로 시작하며 key=value 형태로 옵션 정보를 전달하는 용도
non-option arguments: 단순한 입력 값
CommandLineRunner
CommandLineRunner는 String ... args를 받는 run 메서드 하나를 갖는 @FunctionalInterface이다. 따라서 다음과 같이 생성하면서 빈으로 등록할 수 있다. 이때 args에는 main 메서드에 전달되는 argument 들이 전달되는데 특별히 구분하지 않는다.