What is the role of AbstractJob and SimpleJob, and how does a SimpleJob execute its ordered list of Steps?
answer
- AbstractJob = template method (listeners, status, repo)
- doExecute() is the subclass hook
- SimpleJob = ordered List<Step>, sequential fail-fast
- FlowJob = Flow with on()/to()/split() branching
- StepHandler skips already-COMPLETED steps on restart
basics
~20 sAbstractJob 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// 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 SimpleJobgo deeper
Know SimpleJob just runs steps in order and stops on failure.
Add that AbstractJob is the shared base and the builder picks SimpleJob vs FlowJob for you.
Explain the template-method split (execute vs doExecute), StepHandler restart-skip logic, and SimpleJob vs FlowJob semantics.
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