Skip to content

Commit f7fcfaa

Browse files
committed
Improve batch infrastructure configuration
Before this commit, EnableBatchProcessing was tied to a JDBC infrastructure. Therefore, it was impossible to use a non-JDBC job repository with that annotation. This commit removes the dependency to a JDBC infrastructure from EnableBatchProcessing and introduces new annotations to configure specific job repository implementations. It also updates the programmatic way of configuring infrastructure beans with a base configuration class for each supported job repository implementation. NB: The XML namespace was not changed accordingly as the XSD will not be updated starting from v6. Resolves #4718
1 parent 25525ff commit f7fcfaa

File tree

33 files changed

+769
-428
lines changed

33 files changed

+769
-428
lines changed

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

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@
2525
import org.springframework.batch.core.configuration.support.JobRegistrySmartInitializingSingleton;
2626
import org.springframework.batch.core.configuration.support.MapJobRegistry;
2727
import org.springframework.batch.core.launch.support.JobOperatorFactoryBean;
28-
import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher;
2928
import org.springframework.batch.core.repository.support.JdbcJobRepositoryFactoryBean;
29+
import org.springframework.batch.core.repository.support.MongoJobRepositoryFactoryBean;
30+
import org.springframework.batch.core.repository.support.ResourcelessJobRepository;
3031
import org.springframework.beans.factory.config.BeanDefinition;
3132
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3233
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3334
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
3435
import org.springframework.core.log.LogMessage;
3536
import org.springframework.core.type.AnnotationMetadata;
37+
import org.springframework.transaction.annotation.Isolation;
3638
import org.springframework.util.StopWatch;
3739
import org.springframework.util.StringUtils;
3840

@@ -52,6 +54,8 @@ class BatchRegistrar implements ImportBeanDefinitionRegistrar {
5254

5355
private static final String JOB_REPOSITORY = "jobRepository";
5456

57+
private static final String JOB_OPERATOR = "jobOperator";
58+
5559
private static final String JOB_REGISTRY = "jobRegistry";
5660

5761
private static final String JOB_LOADER = "jobLoader";
@@ -64,7 +68,7 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B
6468
EnableBatchProcessing batchAnnotation = importingClassMetadata.getAnnotations()
6569
.get(EnableBatchProcessing.class)
6670
.synthesize();
67-
registerJobRepository(registry, batchAnnotation);
71+
registerJobRepository(registry, importingClassMetadata);
6872
registerJobRegistry(registry);
6973
registerJobRegistrySmartInitializingSingleton(registry);
7074
registerJobOperator(registry, batchAnnotation);
@@ -82,65 +86,126 @@ private void validateState(AnnotationMetadata importingClassMetadata) {
8286
}
8387
}
8488

85-
private void registerJobRepository(BeanDefinitionRegistry registry, EnableBatchProcessing batchAnnotation) {
89+
private void registerJobRepository(BeanDefinitionRegistry registry, AnnotationMetadata importingClassMetadata) {
8690
if (registry.containsBeanDefinition(JOB_REPOSITORY)) {
8791
LOGGER.info("Bean jobRepository already defined in the application context, skipping"
8892
+ " the registration of a jobRepository");
8993
return;
9094
}
95+
if (importingClassMetadata.hasAnnotation(EnableJdbcJobRepository.class.getName())) {
96+
registerJdbcJobRepository(registry, importingClassMetadata);
97+
}
98+
else {
99+
if (importingClassMetadata.hasAnnotation(EnableMongoJobRepository.class.getName())) {
100+
registerMongoJobRepository(registry, importingClassMetadata);
101+
}
102+
else {
103+
registerDefaultJobRepository(registry);
104+
}
105+
}
106+
}
107+
108+
private void registerJdbcJobRepository(BeanDefinitionRegistry registry, AnnotationMetadata importingClassMetadata) {
109+
EnableJdbcJobRepository jdbcJobRepositoryAnnotation = importingClassMetadata.getAnnotations()
110+
.get(EnableJdbcJobRepository.class)
111+
.synthesize();
91112
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
92113
.genericBeanDefinition(JdbcJobRepositoryFactoryBean.class);
93114

94115
// set mandatory properties
95-
String dataSourceRef = batchAnnotation.dataSourceRef();
116+
String dataSourceRef = jdbcJobRepositoryAnnotation.dataSourceRef();
96117
beanDefinitionBuilder.addPropertyReference("dataSource", dataSourceRef);
97118

98-
String transactionManagerRef = batchAnnotation.transactionManagerRef();
119+
String transactionManagerRef = jdbcJobRepositoryAnnotation.transactionManagerRef();
99120
beanDefinitionBuilder.addPropertyReference("transactionManager", transactionManagerRef);
100121

101122
// set optional properties
102-
String executionContextSerializerRef = batchAnnotation.executionContextSerializerRef();
123+
String executionContextSerializerRef = jdbcJobRepositoryAnnotation.executionContextSerializerRef();
103124
if (registry.containsBeanDefinition(executionContextSerializerRef)) {
104125
beanDefinitionBuilder.addPropertyReference("serializer", executionContextSerializerRef);
105126
}
106127

107-
String conversionServiceRef = batchAnnotation.conversionServiceRef();
128+
String conversionServiceRef = jdbcJobRepositoryAnnotation.conversionServiceRef();
108129
if (registry.containsBeanDefinition(conversionServiceRef)) {
109130
beanDefinitionBuilder.addPropertyReference("conversionService", conversionServiceRef);
110131
}
111132

112-
String incrementerFactoryRef = batchAnnotation.incrementerFactoryRef();
133+
String incrementerFactoryRef = jdbcJobRepositoryAnnotation.incrementerFactoryRef();
113134
if (registry.containsBeanDefinition(incrementerFactoryRef)) {
114135
beanDefinitionBuilder.addPropertyReference("incrementerFactory", incrementerFactoryRef);
115136
}
116137

117-
String jobKeyGeneratorRef = batchAnnotation.jobKeyGeneratorRef();
118-
if (registry.containsBeanDefinition(jobKeyGeneratorRef)) {
119-
beanDefinitionBuilder.addPropertyReference("jobKeyGenerator", jobKeyGeneratorRef);
120-
}
121-
122-
String charset = batchAnnotation.charset();
138+
String charset = jdbcJobRepositoryAnnotation.charset();
123139
if (charset != null) {
124140
beanDefinitionBuilder.addPropertyValue("charset", Charset.forName(charset));
125141
}
126142

127-
String tablePrefix = batchAnnotation.tablePrefix();
143+
String tablePrefix = jdbcJobRepositoryAnnotation.tablePrefix();
128144
if (tablePrefix != null) {
129145
beanDefinitionBuilder.addPropertyValue("tablePrefix", tablePrefix);
130146
}
131147

132-
String isolationLevelForCreate = batchAnnotation.isolationLevelForCreate();
148+
String databaseType = jdbcJobRepositoryAnnotation.databaseType();
149+
if (StringUtils.hasText(databaseType)) {
150+
beanDefinitionBuilder.addPropertyValue("databaseType", databaseType);
151+
}
152+
153+
String jdbcOperationsRef = jdbcJobRepositoryAnnotation.jdbcOperationsRef();
154+
if (registry.containsBeanDefinition(jdbcOperationsRef)) {
155+
beanDefinitionBuilder.addPropertyReference("jdbcOperations", jdbcOperationsRef);
156+
}
157+
158+
beanDefinitionBuilder.addPropertyValue("maxVarCharLength", jdbcJobRepositoryAnnotation.maxVarCharLength());
159+
beanDefinitionBuilder.addPropertyValue("clobType", jdbcJobRepositoryAnnotation.clobType());
160+
beanDefinitionBuilder.addPropertyValue("validateTransactionState",
161+
jdbcJobRepositoryAnnotation.validateTransactionState());
162+
163+
Isolation isolationLevelForCreate = jdbcJobRepositoryAnnotation.isolationLevelForCreate();
164+
if (isolationLevelForCreate != null) {
165+
beanDefinitionBuilder.addPropertyValue("isolationLevelForCreateEnum", isolationLevelForCreate);
166+
}
167+
168+
String jobKeyGeneratorRef = jdbcJobRepositoryAnnotation.jobKeyGeneratorRef();
169+
if (registry.containsBeanDefinition(jobKeyGeneratorRef)) {
170+
beanDefinitionBuilder.addPropertyReference("jobKeyGenerator", jobKeyGeneratorRef);
171+
}
172+
173+
registry.registerBeanDefinition(JOB_REPOSITORY, beanDefinitionBuilder.getBeanDefinition());
174+
}
175+
176+
private void registerMongoJobRepository(BeanDefinitionRegistry registry,
177+
AnnotationMetadata importingClassMetadata) {
178+
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
179+
.genericBeanDefinition(MongoJobRepositoryFactoryBean.class);
180+
EnableMongoJobRepository mongoJobRepositoryAnnotation = importingClassMetadata.getAnnotations()
181+
.get(EnableMongoJobRepository.class)
182+
.synthesize();
183+
String mongoOperationsRef = mongoJobRepositoryAnnotation.mongoOperationsRef();
184+
if (registry.containsBeanDefinition(mongoOperationsRef)) {
185+
beanDefinitionBuilder.addPropertyReference("mongoOperations", mongoOperationsRef);
186+
}
187+
String transactionManagerRef = mongoJobRepositoryAnnotation.transactionManagerRef();
188+
if (registry.containsBeanDefinition(transactionManagerRef)) {
189+
beanDefinitionBuilder.addPropertyReference("transactionManager", transactionManagerRef);
190+
}
191+
Isolation isolationLevelForCreate = mongoJobRepositoryAnnotation.isolationLevelForCreate();
133192
if (isolationLevelForCreate != null) {
134193
beanDefinitionBuilder.addPropertyValue("isolationLevelForCreate", isolationLevelForCreate);
135194
}
136195

137-
String databaseType = batchAnnotation.databaseType();
138-
if (StringUtils.hasText(databaseType)) {
139-
beanDefinitionBuilder.addPropertyValue("databaseType", databaseType);
196+
String jobKeyGeneratorRef = mongoJobRepositoryAnnotation.jobKeyGeneratorRef();
197+
if (registry.containsBeanDefinition(jobKeyGeneratorRef)) {
198+
beanDefinitionBuilder.addPropertyReference("jobKeyGenerator", jobKeyGeneratorRef);
140199
}
200+
beanDefinitionBuilder.addPropertyValue("validateTransactionState",
201+
mongoJobRepositoryAnnotation.validateTransactionState());
141202

142-
beanDefinitionBuilder.addPropertyValue("maxVarCharLength", batchAnnotation.maxVarCharLength());
143-
beanDefinitionBuilder.addPropertyValue("clobType", batchAnnotation.clobType());
203+
registry.registerBeanDefinition(JOB_REPOSITORY, beanDefinitionBuilder.getBeanDefinition());
204+
}
205+
206+
private void registerDefaultJobRepository(BeanDefinitionRegistry registry) {
207+
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
208+
.genericBeanDefinition(ResourcelessJobRepository.class);
144209
registry.registerBeanDefinition(JOB_REPOSITORY, beanDefinitionBuilder.getBeanDefinition());
145210
}
146211

@@ -171,7 +236,7 @@ private void registerJobRegistrySmartInitializingSingleton(BeanDefinitionRegistr
171236
}
172237

173238
private void registerJobOperator(BeanDefinitionRegistry registry, EnableBatchProcessing batchAnnotation) {
174-
if (registry.containsBeanDefinition("jobOperator")) {
239+
if (registry.containsBeanDefinition(JOB_OPERATOR)) {
175240
LOGGER.info("Bean jobOperator already defined in the application context, skipping"
176241
+ " the registration of a jobOperator");
177242
return;
@@ -186,12 +251,16 @@ private void registerJobOperator(BeanDefinitionRegistry registry, EnableBatchPro
186251
beanDefinitionBuilder.addPropertyReference(JOB_REGISTRY, JOB_REGISTRY);
187252

188253
// set optional properties
254+
String taskExecutorRef = batchAnnotation.taskExecutorRef();
255+
if (registry.containsBeanDefinition(taskExecutorRef)) {
256+
beanDefinitionBuilder.addPropertyReference("taskExecutor", taskExecutorRef);
257+
}
189258
String jobParametersConverterRef = batchAnnotation.jobParametersConverterRef();
190259
if (registry.containsBeanDefinition(jobParametersConverterRef)) {
191260
beanDefinitionBuilder.addPropertyReference("jobParametersConverter", jobParametersConverterRef);
192261
}
193262

194-
registry.registerBeanDefinition("jobOperator", beanDefinitionBuilder.getBeanDefinition());
263+
registry.registerBeanDefinition(JOB_OPERATOR, beanDefinitionBuilder.getBeanDefinition());
195264
}
196265

197266
private void registerAutomaticJobRegistrar(BeanDefinitionRegistry registry, EnableBatchProcessing batchAnnotation) {

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

Lines changed: 15 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,15 @@
1515
*/
1616
package org.springframework.batch.core.configuration.annotation;
1717

18-
import java.lang.annotation.Documented;
19-
import java.lang.annotation.ElementType;
20-
import java.lang.annotation.Retention;
21-
import java.lang.annotation.RetentionPolicy;
22-
import java.lang.annotation.Target;
23-
import java.sql.Types;
24-
25-
import javax.sql.DataSource;
26-
2718
import org.springframework.batch.core.configuration.JobRegistry;
2819
import org.springframework.batch.core.configuration.support.ApplicationContextFactory;
2920
import org.springframework.batch.core.configuration.support.AutomaticJobRegistrar;
3021
import org.springframework.batch.core.configuration.support.ScopeConfiguration;
3122
import org.springframework.batch.core.converter.JobParametersConverter;
32-
import org.springframework.batch.core.launch.JobLauncher;
33-
import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher;
3423
import org.springframework.batch.core.repository.JobRepository;
35-
import org.springframework.batch.core.repository.dao.AbstractJdbcBatchMetadataDao;
36-
import org.springframework.batch.support.DatabaseType;
3724
import org.springframework.context.annotation.Import;
38-
import org.springframework.transaction.PlatformTransactionManager;
25+
26+
import java.lang.annotation.*;
3927

4028
/**
4129
* <p>
@@ -67,9 +55,10 @@
6755
* }
6856
* </pre>
6957
*
70-
* This annotation configures JDBC-based Batch infrastructure beans, so you must provide a
71-
* {@link DataSource} and a {@link PlatformTransactionManager} as beans in the application
72-
* context.
58+
* By default,this annotation configures a resouceless batch infrastructure (ie based on a
59+
* {@link org.springframework.batch.core.repository.support.ResourcelessJobRepository} and
60+
* a
61+
* {@link org.springframework.batch.support.transaction.ResourcelessTransactionManager}).
7362
*
7463
* Note that only one of your configuration classes needs to have the
7564
* <code>&#064;EnableBatchProcessing</code> annotation. Once you have an
@@ -83,8 +72,6 @@
8372
* <ul>
8473
* <li>a {@link JobRepository} (bean name "jobRepository" of type
8574
* {@link org.springframework.batch.core.repository.support.SimpleJobRepository})</li>
86-
* <li>a {@link JobLauncher} (bean name "jobLauncher" of type
87-
* {@link TaskExecutorJobLauncher})</li>
8875
* <li>a {@link JobRegistry} (bean name "jobRegistry" of type
8976
* {@link org.springframework.batch.core.configuration.support.MapJobRegistry})</li>
9077
* <li>a {@link org.springframework.batch.core.launch.JobOperator} (bean name
@@ -142,8 +129,8 @@
142129
* </job>
143130
* <beans:bean id="dataSource" .../>
144131
* <beans:bean id="transactionManager" .../>
145-
* <beans:bean id="jobLauncher" class=
146-
"org.springframework.batch.core.launch.support.TaskExecutorJobLauncher">
132+
* <beans:bean id="jobOperator" class=
133+
"org.springframework.batch.core.launch.support.TaskExecutorJobOperator">
147134
* <beans:property name="jobRepository" ref="jobRepository" />
148135
* </beans:bean>
149136
* </batch>
@@ -173,102 +160,26 @@
173160
boolean modular() default false;
174161

175162
/**
176-
* Set the data source to use in the job repository and job explorer.
177-
* @return the bean name of the data source to use. Default to {@literal dataSource}.
178-
*/
179-
String dataSourceRef() default "dataSource";
180-
181-
/**
182-
* Set the type of the data source to use in the job repository. The default type will
183-
* be introspected from the datasource's metadata.
184-
* @since 5.1
185-
* @see DatabaseType
186-
* @return the type of data source.
187-
*/
188-
String databaseType() default "";
189-
190-
/**
191-
* Set the transaction manager to use in the job repository.
192-
* @return the bean name of the transaction manager to use. Defaults to
193-
* {@literal transactionManager}
194-
*/
195-
String transactionManagerRef() default "transactionManager";
196-
197-
/**
198-
* Set the execution context serializer to use in the job repository and job explorer.
199-
* @return the bean name of the execution context serializer to use. Default to
200-
* {@literal executionContextSerializer}.
201-
*/
202-
String executionContextSerializerRef() default "executionContextSerializer";
203-
204-
/**
205-
* The charset to use in the job repository and job explorer
206-
* @return the charset to use. Defaults to {@literal UTF-8}.
207-
*/
208-
String charset() default "UTF-8";
209-
210-
/**
211-
* The Batch tables prefix. Defaults to {@literal "BATCH_"}.
212-
* @return the Batch table prefix
213-
*/
214-
String tablePrefix() default AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX;
215-
216-
/**
217-
* The maximum length of exit messages in the database.
218-
* @return the maximum length of exit messages in the database
219-
*/
220-
int maxVarCharLength() default AbstractJdbcBatchMetadataDao.DEFAULT_EXIT_MESSAGE_LENGTH;
221-
222-
/**
223-
* The incrementer factory to use in various DAOs.
224-
* @return the bean name of the incrementer factory to use. Defaults to
225-
* {@literal incrementerFactory}.
226-
*/
227-
String incrementerFactoryRef() default "incrementerFactory";
228-
229-
/**
230-
* The generator that determines a unique key for identifying job instance objects
231-
* @return the bean name of the job key generator to use. Defaults to
232-
* {@literal jobKeyGenerator}.
233-
*
234-
* @since 5.1
235-
*/
236-
String jobKeyGeneratorRef() default "jobKeyGenerator";
237-
238-
/**
239-
* The type of large objects.
240-
* @return the type of large objects.
241-
*/
242-
int clobType() default Types.CLOB;
243-
244-
/**
245-
* Set the isolation level for create parameter value. Defaults to
246-
* {@literal ISOLATION_SERIALIZABLE}.
247-
* @return the value of the isolation level for create parameter
248-
*/
249-
String isolationLevelForCreate() default "ISOLATION_SERIALIZABLE";
250-
251-
/**
252-
* Set the task executor to use in the job launcher.
163+
* Set the task executor to use in the job operator.
253164
* @return the bean name of the task executor to use. Defaults to
254165
* {@literal taskExecutor}
255166
*/
256167
String taskExecutorRef() default "taskExecutor";
257168

258169
/**
259-
* Set the conversion service to use in the job repository and job explorer. This
260-
* service is used to convert job parameters from String literal to typed values and
261-
* vice versa.
262-
* @return the bean name of the conversion service to use. Defaults to
263-
* {@literal conversionService}
170+
* Set the transaction manager to use in the job operator.
171+
* @return the bean name of the transaction manager to use. Defaults to
172+
* {@literal transactionManager}
264173
*/
265-
String conversionServiceRef() default "conversionService";
174+
String transactionManagerRef() default "transactionManager";
266175

267176
/**
268177
* Set the {@link JobParametersConverter} to use in the job operator.
269178
* @return the bean name of the job parameters converter to use. Defaults to
270179
* {@literal jobParametersConverter}
180+
* @deprecated since 6.0 with no replacement. Scheduled for removal in 6.2 or later
271181
*/
182+
@Deprecated(since = "6.0", forRemoval = true)
272183
String jobParametersConverterRef() default "jobParametersConverter";
273184

274185
}

0 commit comments

Comments
 (0)