Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a7f090a

Browse files
Make MapJobRegistry smart to auto register jobs at startup
Before this commit, it was required to populate the job registry with a different component (like a bean post processor or a smart initializing singleton) before being able to use it with the JobOperator. This commit makes the `MapJobRegistry` smart enough to auto register jobs defined in the application context. This removes the need for a distinct component to populate the registry and therefore simplifies the configuration. This commit also: - removes the usage of `JobFactory` from `JobRegistry` - deprecates JobRegistrySmartInitializingSingleton and removes its configuration from the default batch configuration Resolves #4854 Resolves #4855 Resolves #4856
1 parent 08c4cb1 commit a7f090a

18 files changed

+87
-234
lines changed

‎spring-batch-core/src/main/java/org/springframework/batch/core/configuration/JobRegistry.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2022 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,17 +22,18 @@
2222
* <code>name</code>.
2323
*
2424
* @author Dave Syer
25+
* @author Mahmoud Ben Hassine
2526
*
2627
*/
2728
public interface JobRegistry extends ListableJobLocator {
2829

2930
/**
3031
* Registers a {@link Job} at runtime.
31-
* @param jobFactory the {@link Job} to be registered
32-
* @throws DuplicateJobException if a factory with the same job name has already been
32+
* @param job the {@link Job} to be registered
33+
* @throws DuplicateJobException if a job with the same name has already been
3334
* registered.
3435
*/
35-
void register(JobFactoryjobFactory) throws DuplicateJobException;
36+
void register(Jobjob) throws DuplicateJobException;
3637

3738
/**
3839
* Unregisters a previously registered {@link Job}. If it was not previously

‎spring-batch-core/src/main/java/org/springframework/batch/core/configuration/annotation/BatchRegistrar.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar;
2424
import org.springframework.batch.core.configuration.support.DefaultJobLoader;
25-
import org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton;
2625
import org.springframework.batch.core.configuration.support.MapJobRegistry;
2726
import org.springframework.batch.core.launch.support.JobOperatorFactoryBean;
2827
import org.springframework.batch.core.repository.support.JdbcJobRepositoryFactoryBean;
@@ -70,7 +69,6 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B
7069
.synthesize();
7170
registerJobRepository(registry, importingClassMetadata);
7271
registerJobRegistry(registry);
73-
registerJobRegistrySmartInitializingSingleton(registry);
7472
registerJobOperator(registry, batchAnnotation);
7573
registerAutomaticJobRegistrar(registry, batchAnnotation);
7674
watch.stop();
@@ -220,21 +218,6 @@ private void registerJobRegistry(BeanDefinitionRegistry registry) {
220218
registry.registerBeanDefinition(JOB_REGISTRY, beanDefinition);
221219
}
222220

223-
private void registerJobRegistrySmartInitializingSingleton(BeanDefinitionRegistry registry) {
224-
if (registry.containsBeanDefinition("jobRegistrySmartInitializingSingleton")) {
225-
LOGGER
226-
.info("Bean jobRegistrySmartInitializingSingleton already defined in the application context, skipping"
227-
+ " the registration of a jobRegistrySmartInitializingSingleton");
228-
return;
229-
}
230-
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
231-
.genericBeanDefinition(JobRegistrySmartInitializingSingleton.class);
232-
beanDefinitionBuilder.addPropertyReference(JOB_REGISTRY, JOB_REGISTRY);
233-
234-
registry.registerBeanDefinition("jobRegistrySmartInitializingSingleton",
235-
beanDefinitionBuilder.getBeanDefinition());
236-
}
237-
238221
private void registerJobOperator(BeanDefinitionRegistry registry, EnableBatchProcessing batchAnnotation) {
239222
if (registry.containsBeanDefinition(JOB_OPERATOR)) {
240223
LOGGER.info("Bean jobOperator already defined in the application context, skipping"

‎spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultBatchConfiguration.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@
5050
* <li>a {@link ResourcelessJobRepository} named "jobRepository"</li>
5151
* <li>a {@link MapJobRegistry} named "jobRegistry"</li>
5252
* <li>a {@link TaskExecutorJobOperator} named "JobOperator"</li>
53-
* <li>a {@link JobRegistrySmartInitializingSingleton} named
54-
* "jobRegistrySmartInitializingSingleton"</li>
5553
* <li>a {@link org.springframework.batch.core.scope.StepScope} named "stepScope"</li>
5654
* <li>a {@link org.springframework.batch.core.scope.JobScope} named "jobScope"</li>
5755
* </ul>
@@ -117,21 +115,6 @@ public JobOperator jobOperator(JobRepository jobRepository, JobRegistry jobRegis
117115
}
118116
}
119117

120-
@Bean
121-
public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry)
122-
throws BatchConfigurationException {
123-
JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton = new JobRegistrySmartInitializingSingleton();
124-
jobRegistrySmartInitializingSingleton.setJobRegistry(jobRegistry);
125-
try {
126-
jobRegistrySmartInitializingSingleton.afterPropertiesSet();
127-
return jobRegistrySmartInitializingSingleton;
128-
}
129-
catch (Exception e) {
130-
throw new BatchConfigurationException(
131-
"Unable to configure the default job registry SmartInitializingSingleton", e);
132-
}
133-
}
134-
135118
/**
136119
* Return the transaction manager to use for the job operator. Defaults to
137120
* {@link ResourcelessTransactionManager}.

‎spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/DefaultJobLoader.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2023 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,7 +27,6 @@
2727
import org.springframework.batch.core.Job;
2828
import org.springframework.batch.core.Step;
2929
import org.springframework.batch.core.configuration.DuplicateJobException;
30-
import org.springframework.batch.core.configuration.JobFactory;
3130
import org.springframework.batch.core.configuration.JobRegistry;
3231
import org.springframework.batch.core.configuration.StepRegistry;
3332
import org.springframework.batch.core.launch.NoSuchJobException;
@@ -251,8 +250,7 @@ private Collection<Step> getSteps(final StepLocator stepLocator, final Applicati
251250
* @throws DuplicateJobException if that job is already registered
252251
*/
253252
private void doRegister(ConfigurableApplicationContext context, Job job) throws DuplicateJobException {
254-
final JobFactory jobFactory = new ReferenceJobFactory(job);
255-
jobRegistry.register(jobFactory);
253+
jobRegistry.register(job);
256254

257255
if (stepRegistry != null) {
258256
if (!(job instanceof StepLocator)) {

‎spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobFactoryRegistrationListener.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2023 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
* Generic service that can bind and unbind a {@link JobFactory} in a {@link JobRegistry}.
2828
*
2929
* @author Dave Syer
30+
* @author Mahmoud Ben Hassine
3031
*
3132
*/
3233
public class JobFactoryRegistrationListener {
@@ -53,7 +54,7 @@ public void bind(JobFactory jobFactory, Map<String, ?> params) throws Exception
5354
if (logger.isInfoEnabled()) {
5455
logger.info("Binding JobFactory: " + jobFactory.getJobName());
5556
}
56-
jobRegistry.register(jobFactory);
57+
jobRegistry.register(jobFactory.createJob());
5758
}
5859

5960
/**

‎spring-batch-core/src/main/java/org/springframework/batch/core/configuration/support/JobRegistrySmartInitializingSingleton.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@
4545
*
4646
* @author Henning Pöttker
4747
* @since 5.1.1
48+
* @deprecated since 6.0 with no replacement. Register a {@link MapJobRegistry} as a bean,
49+
* and it will automatically register all {@link Job} beans in the application context.
4850
*/
51+
@Deprecated(since = "6.0", forRemoval = true)
4952
public class JobRegistrySmartInitializingSingleton
5053
implements SmartInitializingSingleton, BeanFactoryAware, InitializingBean, DisposableBean {
5154

@@ -143,12 +146,11 @@ private void postProcessAfterInitialization(Job job, String beanName) {
143146
groupName = getGroupName(defaultListableBeanFactory.getBeanDefinition(beanName), job);
144147
}
145148
job = groupName == null ? job : new GroupAwareJob(groupName, job);
146-
ReferenceJobFactory jobFactory = new ReferenceJobFactory(job);
147-
String name = jobFactory.getJobName();
149+
String name = job.getName();
148150
if (logger.isDebugEnabled()) {
149151
logger.debug("Registering job: " + name);
150152
}
151-
jobRegistry.register(jobFactory);
153+
jobRegistry.register(job);
152154
jobNames.add(name);
153155
}
154156
catch (DuplicateJobException e) {
Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2019 the original author or authors.
2+
* Copyright 2006-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,68 +16,89 @@
1616
package org.springframework.batch.core.configuration.support;
1717

1818
import java.util.Collections;
19+
import java.util.Map;
1920
import java.util.Set;
2021
import java.util.concurrent.ConcurrentHashMap;
2122
import java.util.concurrent.ConcurrentMap;
2223

24+
import org.apache.commons.logging.Log;
25+
import org.apache.commons.logging.LogFactory;
2326
import org.springframework.batch.core.Job;
2427
import org.springframework.batch.core.configuration.DuplicateJobException;
25-
import org.springframework.batch.core.configuration.JobFactory;
2628
import org.springframework.batch.core.configuration.JobRegistry;
2729
import org.springframework.batch.core.launch.NoSuchJobException;
30+
import org.springframework.beans.BeansException;
31+
import org.springframework.beans.factory.SmartInitializingSingleton;
32+
import org.springframework.context.ApplicationContext;
33+
import org.springframework.context.ApplicationContextAware;
2834
import org.springframework.lang.Nullable;
2935
import org.springframework.util.Assert;
3036

3137
/**
32-
* Simple, thread-safe, map-based implementation of {@link JobRegistry}.
38+
* Simple, thread-safe, map-based implementation of {@link JobRegistry}. This registry is
39+
* a {@link SmartInitializingSingleton} that is automatically populated with all
40+
* {@link Job} beans in the {@link ApplicationContext}.
3341
*
3442
* @author Dave Syer
3543
* @author Robert Fischer
3644
* @author Mahmoud Ben Hassine
3745
*/
38-
public class MapJobRegistry implements JobRegistry {
46+
public class MapJobRegistry implements JobRegistry, SmartInitializingSingleton, ApplicationContextAware {
47+
48+
protected final Log logger = LogFactory.getLog(getClass());
3949

4050
/**
41-
* The map holding the registered job factories.
51+
* The map holding the registered jobs.
4252
*/
43-
// The "final" ensures that it is visible and initialized when the constructor
44-
// resolves.
45-
private final ConcurrentMap<String, JobFactory> map = new ConcurrentHashMap<>();
53+
private final ConcurrentMap<String, Job> map = new ConcurrentHashMap<>();
54+
55+
private ApplicationContext applicationContext;
56+
57+
@Override
58+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
59+
this.applicationContext = applicationContext;
60+
}
61+
62+
@Override
63+
public void afterSingletonsInstantiated() {
64+
Map<String, Job> jobBeans = this.applicationContext.getBeansOfType(Job.class);
65+
this.map.putAll(jobBeans);
66+
}
4667

4768
@Override
48-
public void register(JobFactoryjobFactory) throws DuplicateJobException {
49-
Assert.notNull(jobFactory, "jobFactory is null");
50-
String name = jobFactory.getJobName();
51-
Assert.notNull(name, "Job configuration must have a name.");
52-
JobFactory previousValue = map.putIfAbsent(name, jobFactory);
69+
public void register(Jobjob) throws DuplicateJobException {
70+
Assert.notNull(job, "job must not be null");
71+
String jobName = job.getName();
72+
Assert.notNull(jobName, "Job name must not be null");
73+
Job previousValue = this.map.putIfAbsent(jobName, job);
5374
if (previousValue != null) {
54-
throw new DuplicateJobException("A job configuration with this name [" + name + "] was already registered");
75+
throw new DuplicateJobException("A job with this name [" + jobName + "] was already registered");
5576
}
5677
}
5778

5879
@Override
5980
public void unregister(String name) {
60-
Assert.notNull(name, "Job configuration must have a name.");
61-
map.remove(name);
81+
Assert.notNull(name, "Job name must not be null");
82+
this.map.remove(name);
6283
}
6384

6485
@Override
6586
public Job getJob(@Nullable String name) throws NoSuchJobException {
66-
JobFactoryfactory = map.get(name);
67-
if (factory == null) {
68-
throw new NoSuchJobException("No job configuration with the name [" + name + "] was registered");
87+
Jobjob = this.map.get(name);
88+
if (job == null) {
89+
throw new NoSuchJobException("No job with the name [" + name + "] was registered");
6990
}
7091
else {
71-
return factory.createJob();
92+
return job;
7293
}
7394
}
7495

7596
/**
76-
* Provides an unmodifiable view of the job names.
97+
* Provides an unmodifiable view of job names.
7798
*/
7899
@Override
79100
public Set<String> getJobNames() {
80-
return Collections.unmodifiableSet(map.keySet());
101+
return Collections.unmodifiableSet(this.map.keySet());
81102
}
82103

83104
}

‎spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/BatchRegistrarTests.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.springframework.batch.core.DefaultJobKeyGenerator;
2828
import org.springframework.batch.core.JobKeyGenerator;
2929
import org.springframework.batch.core.configuration.JobRegistry;
30-
import org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton;
3130
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
3231
import org.springframework.batch.core.converter.JobParametersConverter;
3332
import org.springframework.batch.core.converter.JsonJobParametersConverter;
@@ -64,8 +63,6 @@ void testConfigurationWithUserDefinedBeans() {
6463
Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobRepository.class)).isMock());
6564
Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobRegistry.class)).isMock());
6665
Assertions.assertTrue(Mockito.mockingDetails(context.getBean(JobOperator.class)).isMock());
67-
Assertions
68-
.assertTrue(Mockito.mockingDetails(context.getBean(JobRegistrySmartInitializingSingleton.class)).isMock());
6966
}
7067

7168
@Test
@@ -147,15 +144,12 @@ void testDefaultInfrastructureBeansRegistration() {
147144
JobRepository jobRepository = context.getBean(JobRepository.class);
148145
JobRegistry jobRegistry = context.getBean(JobRegistry.class);
149146
JobOperator jobOperator = context.getBean(JobOperator.class);
150-
JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton = context
151-
.getBean(JobRegistrySmartInitializingSingleton.class);
152147

153148
// then
154149
Assertions.assertNotNull(jobLauncher);
155150
Assertions.assertNotNull(jobRepository);
156151
Assertions.assertNotNull(jobRegistry);
157152
Assertions.assertNotNull(jobOperator);
158-
Assertions.assertNotNull(jobRegistrySmartInitializingSingleton);
159153
}
160154

161155
@Test
@@ -236,11 +230,6 @@ public JobOperator jobOperator() {
236230
return Mockito.mock();
237231
}
238232

239-
@Bean
240-
public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton() {
241-
return Mockito.mock();
242-
}
243-
244233
}
245234

246235
@Configuration

‎spring-batch-core/src/test/java/org/springframework/batch/core/configuration/support/DefaultBatchConfigurationTests.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2024 the original author or authors.
2+
* Copyright 2022-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@
3636
import org.springframework.batch.core.step.builder.StepBuilder;
3737
import org.springframework.batch.core.step.tasklet.Tasklet;
3838
import org.springframework.batch.repeat.RepeatStatus;
39-
import org.springframework.beans.factory.BeanCreationException;
4039
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4140
import org.springframework.context.annotation.Bean;
4241
import org.springframework.context.annotation.Configuration;
@@ -72,9 +71,6 @@ void testConfigurationWithCustomInfrastructureBean() {
7271
Assertions.assertEquals(1, jobRepositories.size());
7372
JobRepository jobRepository = jobRepositories.entrySet().iterator().next().getValue();
7473
Assertions.assertInstanceOf(DummyJobRepository.class, jobRepository);
75-
Map<String, JobRegistrySmartInitializingSingleton> jobRegistrySmartInitializingSingletonMap = context
76-
.getBeansOfType(JobRegistrySmartInitializingSingleton.class);
77-
Assertions.assertEquals(1, jobRegistrySmartInitializingSingletonMap.size());
7874
}
7975

8076
@Test
@@ -87,15 +83,12 @@ void testDefaultInfrastructureBeansRegistration() {
8783
JobRepository jobRepository = context.getBean(JobRepository.class);
8884
JobRegistry jobRegistry = context.getBean(JobRegistry.class);
8985
JobOperator jobOperator = context.getBean(JobOperator.class);
90-
JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton = context
91-
.getBean(JobRegistrySmartInitializingSingleton.class);
9286

9387
// then
9488
Assertions.assertNotNull(jobLauncher);
9589
Assertions.assertNotNull(jobRepository);
9690
Assertions.assertNotNull(jobRegistry);
9791
Assertions.assertNotNull(jobOperator);
98-
Assertions.assertNotNull(jobRegistrySmartInitializingSingleton);
9992
}
10093

10194
@Configuration
@@ -136,13 +129,6 @@ public JobRepository jobRepository() {
136129
return new DummyJobRepository();
137130
}
138131

139-
@Bean
140-
public JobRegistrySmartInitializingSingleton jobRegistrySmartInitializingSingleton(JobRegistry jobRegistry) {
141-
JobRegistrySmartInitializingSingleton smartInitializingSingleton = new JobRegistrySmartInitializingSingleton();
142-
smartInitializingSingleton.setJobRegistry(jobRegistry);
143-
return smartInitializingSingleton;
144-
}
145-
146132
}
147133

148134
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /