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
This commit is contained in:
Mahmoud Ben Hassine
2024-03-22 15:40:52 +01:00
parent 692e7699c2
commit d9709ef954
7 changed files with 66 additions and 44 deletions

View File

@@ -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) {

View File

@@ -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})</li>
* <li>a
* {@link org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor}
* (bean name "jobRegistryBeanPostProcessor" of type
* {@link org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor})</li>
* {@link org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton}
* (bean name "jobRegistrySmartInitializingSingleton" of type
* {@link org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton})</li>
* </ul>
*
* If the configuration is specified as <code>modular=true</code>, the context also

View File

@@ -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
*/

View File

@@ -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 {

View File

@@ -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();
}

View File

@@ -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<String, JobRegistryBeanPostProcessor> jobRegistryBeanPostProcessorMap = context
.getBeansOfType(JobRegistryBeanPostProcessor.class);
Assertions.assertEquals(1, jobRegistryBeanPostProcessorMap.size());
Map<String, JobRegistrySmartInitializingSingleton> 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;
}
}

View File

@@ -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