-
[Spring] JobRegistryBeanPostProcessor (not eligible for auto-proxying) 관련 경고 트러블 슈팅개발 2024. 9. 11. 21:02
배치 어플리케이션이 필요해서 Spring Boot 3.2 버전으로 Spring Batch 프로젝트를 만들었다.
그런데 못보던 경고 로그가 떠서, 해당 이슈와 관련하여 문제부터 원인파악, 근본적인 해결까지 정리해 보았다.
문제
스프링 배치 5.0 이상 버전부터는 기존의 배치 환경을 구성할 때 사용하는 @EnableBatchProcessing 어노테이션을 프로그래밍 방식으로 대체하는 DefaultBatchConfiguration 클래스를 지원하는데, JobRepository등에 필요한 DataSource를 따로 설정하기 위해 DefaultBatchConfiguration 클래스를 상속하여 아래와 같이 구성하였다.
package day.mercury.batch; import ... @Configuration public class BatchConfiguration extends DefaultBatchConfiguration { @Bean @ConfigurationProperties("spring.datasource.batch") public DataSource batchDataSource() { return DataSourceBuilder.create().build(); } @Bean public PlatformTransactionManager batchTransactionManager() { return new DataSourceTransactionManager(batchDataSource()); } @Override @NonNull protected DataSource getDataSource() { return batchDataSource(); } @Override @NonNull protected PlatformTransactionManager getTransactionManager() { return batchTransactionManager(); } }
그리고 어플리케이션을 실행했는데 처음 보는 WARN로그가 출력되었다.
에러가 아니긴 해도, 최소한 경고가 뜨는 이유는 알고 싶었다.
해당 로그 메세지를 자세히 보면, DefaultBatchConfiguration을 상속한 BatchConfiguration 빈과, JobRegistry가 BeanPostProcessor에 의해 제대로 처리 되지 않았다는 것을 알 수 있다.
그리고 JobRegistryBeanPostProcessor가 non-static 팩터리 메서드로 선언되어 있으니 static 메서드를 사용하라고 하며, JobRegistry 빈이 JobRegistryBeanPostProcessor에 의해 초기에 주입된 것이 아닌지 확인하라고 한다.
BeanPostProcessorChecker : Bean 'batchConfiguration' of type [day.mercury.batch.BatchConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [jobRegistryBeanPostProcessor] is declared through a non-static factory method on that class; consider declaring it as static instead. BeanPostProcessorChecker : Bean 'jobRegistry' of type [org.springframework.batch.core.configuration.support.MapJobRegistry] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [jobRegistryBeanPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.
참고로 BeanPostProcessor(BPP)는 스프링에서 빈의 생명 주기 동안 작업을 수행하게 하는 후처리기 인터페이스다. 빈의 초기화와 초기화 이후 부가적인 처리를 할 수 있게 도와준다.
JobRegistryBeanPostProcessor는 이를 구현하여 JobRegistry가 Job을 등록하고 관리할 수 있도록 후처리한다.
원인
기존에는 스프링 빈들 사이의 의존 관계가 BPP가 처리하기에 부적격하면 INFO 레벨의 로그를 띄웠는데, 스프링 Github Repo에 해당 로그 레벨을 격상해야하지 않겠냐는 이슈가 올라왔다.
그렇게 Spring Framework 6.1부터는 해당 로그가 WARN 레벨로 격상되었고, 부가적인 메세지도 추가되었다.
그리고 Spring Batch 5.1버전에는 @EnableBatchProcessing과 DefaultBatchConfiguration이 JobRegistryBeanPostProcessor를 Auto-configure하는 기능이 추가되었다.
그런데 JobRegistryBeanPostProcessor를 사용하는 방식에 대해 문제를 지적하는 이슈가 올라왔다.
결론적으로 BPP가 다른 빈에 의존하는 경우, 스프링이 의도하지 않은 빈을 초기화 할 수 있는데, JobRegistryBeanPostProcessor가 DefaultBatchConfiguration에 의존관계를 가지고 있었기에 애초부터 문제였었고, Spring Framework 6.1에서 BPP관련 로그 레벨이 격상됨에 따라 명확하게 드러난 것이다.
해결
JobRegistryBeanPostProcessor의 대안으로 JobRegistry에 Job을 등록할 수 있도록 JobRegistrySmartInitializingSingleton이 Spring Batch 5.1.1에 추가되었다.
하지만 나는 BatchConfiguraiton을 DefaultBatchConfiguration을 상속하여 구현하였기 때문에, DefaultBatchConfiguration에 있는 JobRegistryBeanPostProcessor를 제거하는 BeanDefinitionRegistryPostProcessor를 static으로 빈을 등록하고, JobRegistrySmartInitializingSingleton을 설정해주었다.
(JobRegistryBeanPostProcessor를 JobRegistrySmartInitializingSingleton로 완전한 전환은 이전 버전 호환 문제로 이루어지지 못했고, Spring Batch 5.2부터 전환 예정이라고 한다.)
package day.mercury.batch; import ... @Configuration public class BatchConfiguration extends DefaultBatchConfiguration { ... @Bean public static BeanDefinitionRegistryPostProcessor jobRegistryBeanPostProcessorRemover() { return registry -> registry.removeBeanDefinition("jobRegistryBeanPostProcessor"); } @Bean public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) { return new JobRegistrySmartInitializingSingleton(jobRegistry); } }
확인을 위해 간단한 Job을 만들고 JobRegistry에 Job이 잘 등록되는지 확인하는 테스트를 작성했다.
package day.mercury.batch; import ... @Configuration public class SimpleJobConfiguration { private final JobRepository jobRepository; private final PlatformTransactionManager transactionManager; public SimpleJobConfiguration(JobRepository jobRepository, @Qualifier("batchTransactionManager") PlatformTransactionManager transactionManager) { this.jobRepository = jobRepository; this.transactionManager = transactionManager; } @Bean public Job simpleJob() { return new JobBuilder("simpleJob", jobRepository) .start(simpleStep()) .build(); } @Bean public Step simpleStep() { return new StepBuilder("simpleStep", jobRepository) .tasklet((StepContribution contribution, ChunkContext chunkContext) -> RepeatStatus.FINISHED, transactionManager) .build(); } }
package day.mercury.batch; import ... @SpringBootTest class JobRegistryTests { @Autowired private JobRegistry jobRegistry; @Test @DisplayName("JobRegistry에 Job이 등록되어 있어야 한다.") void test() { try { Job job = jobRegistry.getJob("simpleJob"); assertEquals("simpleJob", job.getName()); } catch (NoSuchJobException e) { fail(); } } }
테스트를 성공하고 어플리케이션을 실행해 보았더니,
WARN 로그가 사라졌다!!
마무리
스프링이 역전제어를 통해 ApplicationContext에 Bean들을 복잡한 의존 관계에 따라 어떻게 생성/등록/주입하는 여러가지 기법들을 볼 수 있었던 좋은 기회였다. 참 탄탄하고 멋진 프레임워크인것 같다.
참고
https://www.baeldung.com/spring-not-eligible-for-auto-proxying
https://velog.io/@dailylifecoding/Spring-BeanPostProcessor-Weird-Warning-Log
'개발' 카테고리의 다른 글
[Spring Webflux] #4 스프링 웹플럭스의 코틀린 코루틴 지원 내부 구현 뜯어보기 (0) 2024.07.29 [Spring Webflux] #3 스프링 웹플럭스에서 AOP 제대로 사용하기 (0) 2024.04.11 [Spring Webflux] #2 내가 스프링 웹플럭스를 공부한 방법 (0) 2024.04.10 Docker로 Elasticsearch + Kibana 개발환경 구성하기 (2) 2023.11.24 [Spring Webflux] #1 Reactive Streams 이해하기 (0) 2023.02.25