From d9709ef954b0a304512f56497f541db250182f41 Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Fri, 22 Mar 2024 15:40:52 +0100 Subject: [PATCH] Improve the process of job registration This commit changes the way of populating the default job registry from using a BeanPostProcessor to using a JobRegistrySmartInitializingSingleton. This change resolves warnings about beans being eagerly injected into currently created BeanPostProcessors and prevents lifecycle issues about early bean initializations. It also deprecates JobRegistryBeanPostProcessor in favor of JobRegistrySmartInitializingSingleton. Resolves #4547 --- .../annotation/BatchRegistrar.java | 20 +++++----- .../annotation/EnableBatchProcessing.java | 8 ++-- .../support/DefaultBatchConfiguration.java | 38 ++++++++++++------- .../support/JobRegistryBeanPostProcessor.java | 3 ++ .../annotation/BatchRegistrarTests.java | 13 ++++--- .../DefaultBatchConfigurationTests.java | 21 +++++----- .../ROOT/pages/job/advanced-meta-data.adoc | 7 +++- 7 files changed, 66 insertions(+), 44 deletions(-) diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchRegistrar.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchRegistrar.java index 25d31a319..5a680aaa3 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchRegistrar.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the original author or authors. + * Copyright 2022-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar; import org.springframework.batch.core.configuration.support.DefaultJobLoader; -import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor; +import org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton; import org.springframework.batch.core.configuration.support.MapJobRegistry; import org.springframework.batch.core.explore.support.JobExplorerFactoryBean; import org.springframework.batch.core.launch.support.JobOperatorFactoryBean; @@ -63,7 +63,7 @@ class BatchRegistrar implements ImportBeanDefinitionRegistrar { registerJobExplorer(registry, batchAnnotation); registerJobLauncher(registry, batchAnnotation); registerJobRegistry(registry); - registerJobRegistryBeanPostProcessor(registry); + registerJobRegistrySmartInitializingSingleton(registry); registerJobOperator(registry, batchAnnotation); registerAutomaticJobRegistrar(registry, batchAnnotation); watch.stop(); @@ -225,17 +225,19 @@ class BatchRegistrar implements ImportBeanDefinitionRegistrar { registry.registerBeanDefinition("jobRegistry", beanDefinition); } - private void registerJobRegistryBeanPostProcessor(BeanDefinitionRegistry registry) { - if (registry.containsBeanDefinition("jobRegistryBeanPostProcessor")) { - LOGGER.info("Bean jobRegistryBeanPostProcessor already defined in the application context, skipping" - + " the registration of a jobRegistryBeanPostProcessor"); + private void registerJobRegistrySmartInitializingSingleton(BeanDefinitionRegistry registry) { + if (registry.containsBeanDefinition("jobRegistrySmartInitializingSingleton")) { + LOGGER + .info("Bean jobRegistrySmartInitializingSingleton already defined in the application context, skipping" + + " the registration of a jobRegistrySmartInitializingSingleton"); return; } BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder - .genericBeanDefinition(JobRegistryBeanPostProcessor.class); + .genericBeanDefinition(JobRegistrySmartInitializingSingleton.class); beanDefinitionBuilder.addPropertyReference("jobRegistry", "jobRegistry"); - registry.registerBeanDefinition("jobRegistryBeanPostProcessor", beanDefinitionBuilder.getBeanDefinition()); + registry.registerBeanDefinition("jobRegistrySmartInitializingSingleton", + beanDefinitionBuilder.getBeanDefinition()); } private void registerJobOperator(BeanDefinitionRegistry registry, EnableBatchProcessing batchAnnotation) { diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java index 301d160b6..c4fb3be8d 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -93,9 +93,9 @@ import org.springframework.transaction.PlatformTransactionManager; * "jobOperator" of type * {@link org.springframework.batch.core.launch.support.SimpleJobOperator}) *
  • a - * {@link org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor} - * (bean name "jobRegistryBeanPostProcessor" of type - * {@link org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor})
  • + * {@link org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton} + * (bean name "jobRegistrySmartInitializingSingleton" of type + * {@link org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton}) * * * If the configuration is specified as modular=true, the context also diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultBatchConfiguration.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultBatchConfiguration.java index 4789ccc26..17cf0e391 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultBatchConfiguration.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultBatchConfiguration.java @@ -251,24 +251,12 @@ public class DefaultBatchConfiguration implements ApplicationContextAware { * @return a {@link JobRegistryBeanPostProcessor} * @throws BatchConfigurationException if unable to register the bean * @since 5.1 - * @deprecated Use {@link #jobRegistryBeanPostProcessor(JobRegistry)} instead + * @deprecated Use {@link #jobRegistrySmartInitializingSingleton(JobRegistry)} instead */ @Deprecated(forRemoval = true) public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() throws BatchConfigurationException { - return jobRegistryBeanPostProcessor(jobRegistry()); - } - - /** - * Defines a {@link JobRegistryBeanPostProcessor} bean. - * @return a {@link JobRegistryBeanPostProcessor} bean - * @throws BatchConfigurationException if unable to register the bean - * @since 5.2 - */ - @Bean - public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) - throws BatchConfigurationException { JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor(); - jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry); + jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry()); try { jobRegistryBeanPostProcessor.afterPropertiesSet(); return jobRegistryBeanPostProcessor; @@ -278,6 +266,28 @@ public class DefaultBatchConfiguration implements ApplicationContextAware { } } + /** + * Define a {@link JobRegistrySmartInitializingSingleton} bean. + * @param jobRegistry the job registry to populate + * @throws BatchConfigurationException if unable to register the bean + * @return a bean of type {@link JobRegistrySmartInitializingSingleton} + * @since 5.2 + */ + @Bean + public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) + throws BatchConfigurationException { + JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton = new JobRegistrySmartInitializingSingleton(); + jobRegistrySmartInitializingSingleton.setJobRegistry(jobRegistry); + try { + jobRegistrySmartInitializingSingleton.afterPropertiesSet(); + return jobRegistrySmartInitializingSingleton; + } + catch (Exception e) { + throw new BatchConfigurationException( + "Unable to configure the default job registry SmartInitializingSingleton", e); + } + } + /* * Getters to customize the configuration of infrastructure beans */ diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java index 7b66601fb..4afe2597c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistryBeanPostProcessor.java @@ -45,10 +45,13 @@ import org.springframework.util.Assert; * recommended in cases where this class may cause early bean initializations. You must * include at most one of either of them as a bean. * + * @deprecated since 5.2 in favor of {@link JobRegistrySmartInitializingSingleton}. + * * @author Dave Syer * @author Mahmoud Ben Hassine * */ +@Deprecated(since = "5.2") public class JobRegistryBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean, DisposableBean { diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/BatchRegistrarTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/BatchRegistrarTests.java index f26b80d3e..f44881c11 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/BatchRegistrarTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/BatchRegistrarTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the original author or authors. + * Copyright 2022-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ import org.springframework.aop.framework.Advised; import org.springframework.batch.core.DefaultJobKeyGenerator; import org.springframework.batch.core.JobKeyGenerator; import org.springframework.batch.core.configuration.JobRegistry; -import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor; +import org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton; import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.JobOperator; @@ -79,7 +79,7 @@ class BatchRegistrarTests { Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobLauncher.class)).isMock()); Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobRegistry.class)).isMock()); Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobOperator.class)).isMock()); - Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobRegistryBeanPostProcessor.class)).isMock()); + Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobRegistrySmartInitializingSingleton.class)).isMock()); } @Test @@ -162,7 +162,8 @@ class BatchRegistrarTests { JobExplorer jobExplorer = context.getBean(JobExplorer.class); JobRegistry jobRegistry = context.getBean(JobRegistry.class); JobOperator jobOperator = context.getBean(JobOperator.class); - JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = context.getBean(JobRegistryBeanPostProcessor.class); + JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton = context + .getBean(JobRegistrySmartInitializingSingleton.class); // then Assertions.assertNotNull(jobLauncher); @@ -170,7 +171,7 @@ class BatchRegistrarTests { Assertions.assertNotNull(jobExplorer); Assertions.assertNotNull(jobRegistry); Assertions.assertNotNull(jobOperator); - Assertions.assertNotNull(jobRegistryBeanPostProcessor); + Assertions.assertNotNull(jobRegistrySmartInitializingSingleton); } @Test @@ -249,7 +250,7 @@ class BatchRegistrarTests { } @Bean - public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor() { + public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton() { return Mockito.mock(); } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/DefaultBatchConfigurationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/DefaultBatchConfigurationTests.java index d80dd2171..b660c40b8 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/DefaultBatchConfigurationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/DefaultBatchConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the original author or authors. + * Copyright 2022-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -85,9 +85,9 @@ class DefaultBatchConfigurationTests { Assertions.assertEquals(1, jobRepositories.size()); JobRepository jobRepository = jobRepositories.entrySet().iterator().next().getValue(); Assertions.assertInstanceOf(DummyJobRepository.class, jobRepository); - Map jobRegistryBeanPostProcessorMap = context - .getBeansOfType(JobRegistryBeanPostProcessor.class); - Assertions.assertEquals(1, jobRegistryBeanPostProcessorMap.size()); + Map jobRegistrySmartInitializingSingletonMap = context + .getBeansOfType(JobRegistrySmartInitializingSingleton.class); + Assertions.assertEquals(1, jobRegistrySmartInitializingSingletonMap.size()); } @Test @@ -101,7 +101,8 @@ class DefaultBatchConfigurationTests { JobExplorer jobExplorer = context.getBean(JobExplorer.class); JobRegistry jobRegistry = context.getBean(JobRegistry.class); JobOperator jobOperator = context.getBean(JobOperator.class); - JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = context.getBean(JobRegistryBeanPostProcessor.class); + JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton = context + .getBean(JobRegistrySmartInitializingSingleton.class); // then Assertions.assertNotNull(jobLauncher); @@ -109,7 +110,7 @@ class DefaultBatchConfigurationTests { Assertions.assertNotNull(jobExplorer); Assertions.assertNotNull(jobRegistry); Assertions.assertNotNull(jobOperator); - Assertions.assertNotNull(jobRegistryBeanPostProcessor); + Assertions.assertNotNull(jobRegistrySmartInitializingSingleton); } @Configuration @@ -161,10 +162,10 @@ class DefaultBatchConfigurationTests { } @Bean - public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) { - JobRegistryBeanPostProcessor postProcessor = new JobRegistryBeanPostProcessor(); - postProcessor.setJobRegistry(jobRegistry); - return postProcessor; + public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) { + JobRegistrySmartInitializingSingleton smartInitializingSingleton = new JobRegistrySmartInitializingSingleton(); + smartInitializingSingleton.setJobRegistry(jobRegistry); + return smartInitializingSingleton; } } diff --git a/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc b/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc index bd41a5d94..bfa7ff3d1 100644 --- a/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc +++ b/spring-batch-docs/modules/ROOT/pages/job/advanced-meta-data.adoc @@ -222,7 +222,12 @@ example has been given an `id` so that it can be included in child contexts (for example, as a parent bean definition) and cause all jobs created there to also be registered automatically. -As of version 5.1, the `@EnableBatchProcessing` annotation automatically registers a `jobRegistryBeanPostProcessor` bean in the application context. +[WARNING] +.Deprecation +==== +As of version 5.2, the `JobRegistryBeanPostProcessor` class is deprecated in favor of +`JobRegistrySmartInitializingSingleton`, see xref:#jobregistrysmartinitializingsingleton[JobRegistrySmartInitializingSingleton]. +==== [[jobregistrysmartinitializingsingleton]] === JobRegistrySmartInitializingSingleton