2025.01.21 - [Spring Framework/Spring boot] - Spring Boot @Lazy를 사용한 Bean 지연 초기화하기
위 글에서는 특정 빈 클래스를 Lazy 설정하는 방법에 대해 작성했습니다.
간단히 @Lazy 어노테이션을 통해 빈 초기화 시점을 늦추는 식으로 처리했습니다.
이번 글에서는 BeanFactoryPostProcessor 인터페이스에 대해 알아보고 글로벌 @Lazy 설정 방법과 BeaDefinition에 접근하는 방법에 대해 작성해 보겠습니다.
BeanFactoryPostProcessor
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
Factory hook that allows for custom modification of an application context's bean definitions,
A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances.
Doing so may cause premature bean instantiation, violating the container and causing unintended side effects.
If bean instance interaction is required, consider implementing BeanPostProcessor instead.
BeanFactoryPostProcessor의 목적은 빈을 컨텍스트에 초기화하기 전에 빈 정의에 수정 작업을 돕는 것이다.
빈 인스턴스화의 역할은 수행하지 않으며, postProcessBeanFactory() 메서드를 통해 초기화된 빈의 정의를 Modify 역할을 수행합니다.
postProcessBeanFactory()
위 메서드를 통해 빈이 초기화된 이후에 BeanDefinition 수정 작업을 처리할 수 있습니다. (Bean Instantiation 상태가 아니며 빈이 등록되기 전에 필요한 설정을 할 수 있다.)
글로벌적 Lazy 설정하기
나의 경우 멀티 모듈 환경에서 batch성 모듈을 실행시켜야 했다.
batch 모듈이 common 모듈을 의존하고 있는 구조였는데, common 모듈에 불필요한 Bean이 많이 설정되어 있어 이를 Lazy 설정을 하고자 했다.
- BeanFactoryPostProcessor을 통해 BeanClassName이 common 모듈에 있는 속한 것이면 Lazy 설정을 했다.
common 모듈 Lazy 설정한 이유:
- batch 모듈에서 common 모듈을 의존하는데, 불필요한 빈들이 함께 로드된다.
- batch의 경우 Cron 패턴으로 한 번 애플리케이션을 실행하고 종료되는 구조여서, 필요한 빈 클래스만 초기화되도록 한다.
전역적으로 common에 있는 빈 클래스를 Lazy 하게 설정하고, batch 실행 시 필요한 빈들을 초기화하여 사용할 수 있게 한다. 사용되는 시점에 빈들이 초기화되는데, Task가 사용하는 common 빈 클래스가 많지 않아 초기화 비용이 크게 들지 않는다.
@Configuration
public class LazyLoadingConfig {
@Bean
public static BeanFactoryPostProcessor lazyLoadingPostProcessor() {
return beanFactory -> {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// Common 모듈 빈만 지연 로딩 설정
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null && beanClassName.startsWith("com.common")) {
beanDefinition.setLazyInit(true);
}
}
};
}
}
BeanDefinition.getBeanClassName()을 하게 되면 "com.common.framework.ApplicationContextProvider" 형태로 빈 위치와 이름이 반환된다.
common 모듈에서 Lazy 설정된 Bean 확인하기
beanClassName = com.common.framework.ApplicationContextProvider
beanClassName = com.common.framework.i18n.MessageSourceConfig$$SpringCGLIB$$0
beanClassName = com.common.framework.i18n.MessageSourceServiceImpl
beanClassName = com.common.framework.logging.ServerLoggingFilter
beanClassName = com.common.framework.logging.ServerLoggingInterceptor
beanClassName = com.common.framework.security.ForbiddenAuthorityHandler
...
추가로, BeanPostProcessor 알아보기
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
위 형태로 2개의 메서드를 가집니다. 메서드명을 통해 빈이 초기화 되기 전과 후에 추가 설정을 할 수 있음을 알 수 있습니다.
Factory hook that allows for custom modification of new bean instances — for example, checking for marker interfaces or wrapping beans with proxies.
BeanPostProcessor는 BeanFactoryPostProcessor와 다르게 빈이 등록되기 전/후에 처리를 할 수 있습니다.
공식문서에는 이를 통해 proxy를 wrapping 하거나 빈을 checking 할 수 있다.
참고자료
'Spring Framework > Spring boot' 카테고리의 다른 글
Spring Boot @Lazy를 사용한 Bean 지연 초기화하기 (0) | 2025.01.21 |
---|---|
스프링부트 @SpringBootTest, @ContextConfiguration, @WebMvcTest, @AutoConfigureMockMvc 주요 테스트 어노테이션 정리 (0) | 2024.11.27 |
Spring Boot Open-In-View 설정과 데이터베이스 성능 최적화, 영속성 컨텍스트 활용법 (1) | 2024.10.26 |
[SpringBoot] Spring에서 @Transactional과 try-catch 사용 시 롤백되지 않는 이유 (0) | 2024.10.09 |
[Spring] H2 In-memory 데이터베이스 설정 및 접속 방법 (3) | 2024.10.05 |