skip to content

What is the role of AbstractJob and SimpleJob, and how does a SimpleJob execute its ordered list of Steps?

level: seniorimportance: should knowfreq 55%

answer

  1. AbstractJob = template method (listeners, status, repo)
  2. doExecute() is the subclass hook
  3. SimpleJob = ordered List<Step>, sequential fail-fast
  4. FlowJob = Flow with on()/to()/split() branching
  5. StepHandler skips already-COMPLETED steps on restart

basics

~20 s

AbstractJob is the base Job implementation holding shared logic (listeners, status, restart). SimpleJob extends it with a plain ordered list of Steps, executing them one at a time in order and stopping if one fails.

solid answer

~40 s

`AbstractJob` is the abstract base class implementing the `Job` interface. It provides a template-method `execute(JobExecution)` that handles the cross-cutting concerns: firing job listeners, managing `BatchStatus`/`ExitStatus`, validating parameters, and persisting to the JobRepository — then delegates the actual step orchestration to the abstract `doExecute(JobExecution)`. Two concrete subclasses fill that in: **`SimpleJob`** runs a fixed, ordered `List<Step>` sequentially, and **`FlowJob`** delegates to a `Flow` for conditional/parallel transitions. In a `SimpleJob`, `doExecute` iterates the steps in declared order; for each it uses a `StepHandler` to run the step (skipping steps already COMPLETED on restart), inspects the resulting `StepExecution` status, and stops the loop if a step does not complete successfully, propagating that failure to the JobExecution. So a SimpleJob is literally the 'ordered container of steps' model with fail-fast sequential semantics.

code

java · 12 lines
java
// Conceptual shape of what the builder assembles (you don't normally write this):
SimpleJob job = new SimpleJob("importJob");
job.setJobRepository(jobRepository);
job.setSteps(List.of(loadStep, reportStep)); // ordered container
// AbstractJob.execute(jobExecution) -> doExecute() iterates steps in order,
// stops on the first non-successful StepExecution.

// Equivalent, idiomatic form:
Job same = new JobBuilder("importJob", jobRepository)
        .start(loadStep)
        .next(reportStep)
        .build(); // returns a SimpleJob

go deeper

for a junior

Know SimpleJob just runs steps in order and stops on failure.

for a middle

Add that AbstractJob is the shared base and the builder picks SimpleJob vs FlowJob for you.

for a senior

Explain the template-method split (execute vs doExecute), StepHandler restart-skip logic, and SimpleJob vs FlowJob semantics.

for a principal

Reason about transaction boundaries, restart identity via JobInstance, and when a flow graph vs a linear job is the right orchestration model.

**`Job` is an interface; `AbstractJob` is its abstract base implementation** (`org.springframework.batch.core.job.AbstractJob`). It exists so that all Job types share the same lifecycle plumbing while differing only in *how steps are sequenced*. **What AbstractJob provides (the template method):** its `execute(JobExecution execution)` is `final`-ish orchestration that: 1. runs the `JobParametersValidator`, 2. sets `BatchStatus` to STARTED and notifies registered `JobExecutionListener`s (`beforeJob`), 3. calls the abstract **`doExecute(JobExecution)`** — the subclass hook, 4. computes the final `BatchStatus`/`ExitStatus`, 5. fires `afterJob` listeners and updates the JobRepository so the outcome is persisted. AbstractJob also holds `isRestartable()`, the `JobParametersIncrementer`, the validator, and a reference to the `JobRepository`. This is a classic **Template Method** pattern: invariant lifecycle in the base, variant step-sequencing in subclasses. **SimpleJob.** `SimpleJob extends AbstractJob` and models the *linear ordered container*. Internally it keeps a `List<Step> steps`. Its `doExecute` walks that list in order and, for each step, calls `handleStep(step, execution)` which delegates to a `StepHandler` (typically `SimpleStepHandler`). The handler: - checks the JobRepository for a prior `StepExecution` of that step in this JobInstance; if the step already COMPLETED and isn't `allowStartIfComplete`, it is **skipped** (restart optimization); - otherwise it runs `step.execute(stepExecution)`; - returns the `StepExecution`, whose `BatchStatus` the job inspects. If a step ends in anything other than success (FAILED/STOPPED), the SimpleJob **stops iterating** and the JobExecution takes on that terminal status — this is the **fail-fast sequential** contract. **FlowJob (contrast).** The other AbstractJob subclass, `FlowJob`, does not hold a simple list; it delegates to a `Flow` object built from `.on(exitCode).to(step)` transitions, decisions, and `split()` for parallelism. That's how conditional branching and parallel steps are expressed — the plain `SimpleJob` cannot branch. **Why this matters / when to care:** - The builder chooses the subclass for you: `JobBuilder.start(step).next(step)` yields a `SimpleJob`; `.flow(...)`/`.split(...)`/`.on(...).to(...)` yields a `FlowJob`. You rarely instantiate either directly. - Understanding that step-skipping-on-restart lives in the `StepHandler`/JobRepository interaction (not magic) explains why restarts require the same JobInstance identity and a persistent JobRepository. **Gotchas:** 1. A `SimpleJob` is strictly sequential and fail-fast — you cannot get 'continue on failure' or branching without the flow model. 2. Steps skipped on restart are skipped because they are recorded COMPLETED; if you set `allowStartIfComplete(true)` on a step, it re-runs every time even on a fresh restart. 3. The Job's transaction boundary is not around the whole job — each Step manages its own chunk transactions; AbstractJob only wraps metadata updates, so a mid-job crash leaves earlier committed steps' data in place (that's what makes restart meaningful).

  • Which design pattern does AbstractJob use, and what varies between its subclasses?
    Template Method: AbstractJob.execute() fixes the lifecycle (validation, listeners, status, repository updates) and delegates the variant part — how steps are sequenced — to the abstract doExecute(), which SimpleJob (ordered list) and FlowJob (flow graph) implement differently.
  • How does a SimpleJob know to skip a step on restart?
    The StepHandler consults the JobRepository for a prior StepExecution of that step in the same JobInstance; if it's COMPLETED and not allowStartIfComplete, the step is skipped and the job resumes at the first non-completed step.

saying these in an interview costs you the question

  • Claiming SimpleJob can branch conditionally without the flow DSL
  • Saying the whole Job runs in one big transaction
  • Thinking AbstractJob iterates the steps itself (it delegates to doExecute)
  • Believing FlowJob and SimpleJob are unrelated rather than both AbstractJob subclasses

context