Spring Batch Job Configuration: Restart, Incrementer, and Validator
Objective
Knowing the Job/Step model (covered by spring-batch-job-model) is only half the
story — a job also needs to say whether it can be restarted, how new launch
parameters get generated for repeated runs, and whether a launch should be rejected
outright for missing parameters. The book expresses all three as attributes and
child elements of its XML <job>/<step> vocabulary; today the same three
concerns are expressed as method calls on JobBuilder.
Use Cases
- Marking a one-shot job (e.g., a destructive data-migration job) as non-restartable so a second launch attempt fails loudly instead of silently re-running destructive work.
- Auto-generating a fresh, always-unique parameter (like a run date) for each new launch of a recurring job, without the caller having to compute it themselves.
- Rejecting a job launch immediately — before any step runs — when a required
parameter (like a
date) is missing, instead of failing deep inside step logic.
Deep Dive
Restart behavior: restartable attribute → preventRestart()
The book's XML defaults every job to restartable and opts out per job:
plaintext<batch:job id="importProductsJob" restartable="false"> ... </batch:job>
The Java equivalent keeps the same default (restartable) and opts out with a single builder call — no boolean flag to get backwards:
java@Bean
public Job importProductsJob(JobRepository jobRepository, Step readWrite) {
return new JobBuilder("importProductsJob", jobRepository)
.preventRestart()
.start(readWrite)
.build();
}A job built with .preventRestart() throws JobRestartException on any attempt to
launch it again, exactly like the XML version — the guarantee is identical, only
the spelling changed.
Generating fresh parameters: incrementer attribute → .incrementer(...)
JobLauncher.run(...) (or today's JobOperator.start(...)) never invents
parameters on its own — something has to supply them. For jobs launched
repeatedly with parameters that must differ each time, a JobParametersIncrementer
computes the next value from the last:
javapublic interface JobParametersIncrementer {
JobParameters getNext(JobParameters parameters);
}java@Bean
public Job importProductsJob(JobRepository jobRepository, Step readWrite) {
return new JobBuilder("importProductsJob", jobRepository)
.incrementer(new CustomIncrementer())
.start(readWrite)
.build();
}This only matters when the launch mechanism explicitly asks for "the next
instance" (historically via JobOperator.startNextInstance); a caller that always
supplies its own explicit parameters (e.g., an externally computed date) doesn't
need an incrementer at all.
Rejecting bad launches up front: <validator> → .validator(...)
javapublic interface JobParametersValidator {
void validate(JobParameters parameters) throws JobParametersInvalidException;
}java@Bean
public Job importProductsJob(JobRepository jobRepository, Step readWrite) {
return new JobBuilder("importProductsJob", jobRepository)
.validator(parametersValidator())
.start(readWrite)
.build();
}
@Bean
public JobParametersValidator parametersValidator() {
var validator = new DefaultJobParametersValidator();
validator.setRequiredKeys(new String[]{"date"});
validator.setOptionalKeys(new String[]{"productId"});
return validator;
}DefaultJobParametersValidator (unchanged from the book's version) covers the
common case — required vs. optional keys — without writing a custom validator
class; the job fails with JobParametersInvalidException before any step runs if
a required key is missing.
Step sequencing and "parent"/"abstract" configuration reuse
The book's next attribute on <step> chains steps declaratively; the Java form
is the same .next(...) chain already used to build linear or branching flows
(see spring-batch-job-instance-execution-flow). Where the book's XML parent/
abstract attributes let one step or job configuration extend another to avoid
repetition, Java configuration has no dedicated "abstract job" mechanism — the
same reuse is achieved with ordinary code reuse (a shared builder method, a base
@Configuration class, or a helper that returns a partially-configured
JobBuilder/StepBuilder), which is arguably more natural in a language that
already has inheritance and methods for exactly that purpose.
Trade-offs
- The XML vocabulary and the
JobBuilderAPI express the same configuration surface, but the Java form keeps the identifier and the class it refers to (an incrementer, a validator, a listener) in one compiled, refactor-safe unit. A typo in an XMLrefattribute only surfaces at job-startup time; a typo in a Java method call fails to compile. preventRestart()/incrementer()/validator()all default to "off" when omitted, matching the book's XML defaults exactly — nothing about their semantics changed, only the configuration surface moved from markup to method calls.- Losing
parent/abstractjob inheritance isn't a real loss — it was XML's workaround for not having language-level reuse in the first place; Java configuration doesn't need an equivalent because ordinary methods and class hierarchies already do the job, usually more legibly. - Book vs. today: the whole XML vocabulary this chapter documents
(
<job>,<step>,<batch:validator>, and their attributes) is deprecated since Spring Batch 6.0, with removal planned for 7.0 — already noted inspring-batch-chunk-processing. This concept focuses on the specific attributes Chapter 3 adds (restart, incrementer, validator) rather than re-covering the namespace deprecation itself.