이번 포스팅에서는 지연 초기화에 대해 알아보겠습니다.
스프링 빈의 기본 초기화는 즉시 초기화 입니다.
아래 코드를 보며 설명을 드리자면
package com.in28minutes.learnspringframework.example.d1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
class ClassA {
}
@Component
class ClassB {
private ClassA classA;
public ClassB(ClassA classA) {
//Logic
System.out.println("Some Initialization logic");
this.classA = classA;
}
}
@Configuration
@ComponentScan
public class LazyInitializationLauncherApplication {
public static void main(String[] args) {
try (var context = new AnnotationConfigApplicationContext
(LazyInitializationLauncherApplication.class)) {
Arrays.stream(context.getBeanDefinitionNames())
.forEach(System.out::println);
}
}
}
실행 결과로 Class B 가 생성될 때 ClassA를 주입받아 즉시 초기화하는 걸 알 수 있습니다.
Some Initialization logic //즉시 초기화
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
lazyInitializationLauncherApplication
classA
classB
Process finished with exit code 0
여기서 초기화를 늦추고 사용 시점에 초기화를 하고 싶다면?? 어떻게 해야 할까요? 바로 @Lazy 어노테이션을 사용하면 됩니다.
package com.in28minutes.learnspringframework.example.d1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
class ClassA {
}
@Component
@Lazy
class ClassB {
private ClassA classA;
public ClassB(ClassA classA) {
//Logic
System.out.println("Some Initialization logic");
this.classA = classA;
}
}
@Configuration
@ComponentScan
public class LazyInitializationLauncherApplication {
public static void main(String[] args) {
try (var context = new AnnotationConfigApplicationContext
(LazyInitializationLauncherApplication.class)) {
}
}
}
코드를 실행해 보시면 콘솔창에 아무것도 나오지 않는 것을 확인할 수 있습니다.
@Lazy 어노테이션을 추가 했기 때문에 Spring Bean을 사용하기 전에는 초기화를 하지 않기 떄문입니다.
그럼 이번에는 ClassB 스프링 빈을 사용해 보겠습니다. doSomething() 메서드를 정의하고 사용해 봅시다.
package com.in28minutes.learnspringframework.example.d1;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
class ClassA {
}
@Component
@Lazy
class ClassB {
private ClassA classA;
public ClassB(ClassA classA) {
//Logic
System.out.println("Some Initialization logic");
this.classA = classA;
}
public void doSomething() {
System.out.println("Do Something");
}
}
@Configuration
@ComponentScan
public class LazyInitializationLauncherApplication {
public static void main(String[] args) {
try (var context = new AnnotationConfigApplicationContext
(LazyInitializationLauncherApplication.class)) {
System.out.println("Initialization of context is completed");
context.getBean(ClassB.class).doSomething();
}
}
}
실행 결과
Initialization of context is completed
Some Initialization logic
Do Something
먼저 컨텍스트가 초기화 되었다는 문장이 나온 뒤 Bean을 사용하기 바로 전에 Bean을 로드합니다. Bean을 사용하려고 할 때 로드되는 겁니다
스프링 빈의 기본 초기화는 즉시 초기화 이고 스프링 빈의 사용을 늦추고 싶다면 @Lazy 어노테이션을 사용하면 됩니다.
하지만, 추천하는 초기화는 즉시 초기화입니다. Why?? 생성 시점에 초기화를 하면 애플리케이션이 실행될때 바로 에러를 발견할 수 있지만 지연 초기화를 사용하면 애플리케이션 실행 시점에 에러를 발견할 수 없습니다. Bean사용 시점에 초기화를 하기 때문에 런타임시에 에러가 날 수 있습니다.