skip to content

Listeners

Listeners hook the job, step, chunk and item lifecycle, which is how batch jobs get logging, metrics and notifications. Interviewers ask where you would put the 'job finished with N errors' alert.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

5

What is a JobExecutionListener in Spring Batch, and when do its callbacks run?

level: juniorimportance: must knowfreq 55%

answer

  1. beforeJob / afterJob
  2. afterJob runs even on FAILED
  3. JobExecution -> BatchStatus/ExitStatus
  4. JobBuilder.listener(...)
  5. @BeforeJob / @AfterJob annotation form

basics

~10 s

A JobExecutionListener lets you run code around a job. beforeJob runs before the job starts; afterJob runs once the job finishes, whether it succeeded or failed. You register it on the job builder.

solid answer

~30 s

JobExecutionListener is a lifecycle hook with two callbacks: beforeJob(JobExecution) runs once before any step starts, and afterJob(JobExecution) runs once after the job completes. Crucially afterJob runs regardless of outcome — COMPLETED, FAILED, or STOPPED — so it's the right place for cleanup, notifications, or inspecting the final BatchStatus/ExitStatus. You register it with JobBuilder.listener(...). You can either implement the interface (its methods are default methods in Spring Batch 5, so override only what you need) or annotate plain methods with @BeforeJob/@AfterJob. Common uses: sending a completion email, logging start/end timestamps, initializing shared resources, or writing summary metrics from the JobExecution's ExecutionContext.

code

java · 21 lines
java
public class NotifyingJobListener implements JobExecutionListener {

    @Override
    public void beforeJob(JobExecution jobExecution) {
        jobExecution.getExecutionContext().put("startedAt", System.currentTimeMillis());
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        // Runs on success AND failure
        if (jobExecution.getStatus() == BatchStatus.FAILED) {
            // send alert
        }
    }
}

// Registration
Job job = new JobBuilder("reportJob", jobRepository)
        .listener(new NotifyingJobListener())
        .start(step1)
        .build();

go deeper

for a junior

Know the two callbacks and that afterJob runs on success or failure.

for a middle

Know both registration styles and that JobExecution carries BatchStatus/ExitStatus and an ExecutionContext.

for a senior

Discuss idempotent cleanup, seeding vs reading the job ExecutionContext, and behavior when beforeJob throws.

for a principal

Frame it as the outermost of Batch's nested listener scopes and where job-level observability/metrics and resource lifecycles belong.

## What it is Spring Batch models a **Job** as a container of one or more **Steps**. A `JobExecutionListener` is a lifecycle hook that lets your code observe the job as a whole, outside the steps. It has two callbacks: - `void beforeJob(JobExecution jobExecution)` — invoked once, before the first step runs. - `void afterJob(JobExecution jobExecution)` — invoked once, after the last step finishes. Both receive the `JobExecution`, the runtime object describing this run: its `BatchStatus` (COMPLETED / FAILED / STOPPED / etc.), its `ExitStatus`, start/end times, the list of `StepExecution`s, and a job-level `ExecutionContext` (a key/value bag persisted in the job repository). ## Key semantic: afterJob always runs `afterJob` runs **whether the job succeeded or failed** (as long as the job actually started). That makes it the canonical place for cleanup and end-of-job reporting. You typically branch on status: ```java if (jobExecution.getStatus() == BatchStatus.FAILED) { /* alert */ } ``` ## Two ways to write one 1. **Implement the interface.** In Spring Batch 5 the methods are `default`, so you override only the one you care about. 2. **Annotation form.** Put `@BeforeJob` / `@AfterJob` on plain methods of any bean and register that bean; Spring wraps it via `JobListenerFactoryBean`. The annotated method may take a `JobExecution` parameter or no parameter. ## Registration ```java new JobBuilder("myJob", jobRepository) .listener(myJobListener) .start(step1) .build(); ``` ## When to use - Send a start/finish notification or email. - Log or export job-level metrics (durations, counts). - Seed the job `ExecutionContext` before steps, or read aggregated results after. - Acquire/release a resource that spans the whole job. ## Gotchas - Don't put per-record logic here — it fires once per job, not per item; item/step listeners exist for finer scopes. - `beforeJob` is not called if the job fails to launch (e.g., validation of `JobParameters` fails before execution). Once a `JobExecution` exists and starts, both callbacks fire. - Exceptions thrown from `beforeJob` fail the job before any step runs.

  • Does afterJob run if the job fails halfway through a step?
    Yes. As long as the job started, afterJob is always invoked with the final JobExecution — you inspect getStatus()/getExitStatus() to see it FAILED. It only wouldn't run if the job never launched (e.g., pre-launch parameter validation failure).
  • How would you use the annotation form instead of implementing the interface?
    Put @BeforeJob and/or @AfterJob on plain methods of a bean (optionally taking a JobExecution parameter) and pass that bean to JobBuilder.listener(...). Spring wraps it via JobListenerFactoryBean so no interface is needed.

saying these in an interview costs you the question

  • Claiming afterJob only runs on success
  • Thinking beforeJob/afterJob fire per step or per item
  • Confusing JobExecutionListener with StepExecutionListener scope

context

open as a page

How does StepExecutionListener work, and what is the @BeforeStep annotation commonly used for?

level: middleimportance: must knowfreq 50%

basics

~20 s

StepExecutionListener has beforeStep and afterStep callbacks that run around a single step. @BeforeStep is often put on a reader or writer method to grab the StepExecution — and through it the JobParameters — so the component can configure itself.

open as a page

What are the ChunkListener callbacks, and how do they relate to the chunk's transaction boundaries?

level: seniorimportance: should knowfreq 40%

basics

~10 s

ChunkListener has beforeChunk, afterChunk, and afterChunkError. beforeChunk runs at the start of the chunk inside its transaction; afterChunk runs after that transaction commits successfully; afterChunkError runs if the chunk failed and rolled back.

open as a page

Explain the item-level listeners (ItemReadListener, ItemProcessListener, ItemWriteListener) and their error callbacks.

level: seniorimportance: should knowfreq 42%

basics

~20 s

These three listeners hook the read, process, and write phases of each chunk. Each has a before, an after, and an onError callback — for example ItemReadListener has beforeRead, afterRead(item), and onReadError(exception) — so you can log, count, or inspect failures at each stage.

open as a page

How are Batch listeners registered, and what are the design trade-offs between the interface form, the annotation form, and listener ordering/nesting?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

You register listeners on the builders — JobBuilder.listener for job listeners, StepBuilder.listener for step/chunk/item listeners. You can implement the interface or annotate methods (@BeforeStep, @AfterChunk, etc.); Spring's factory beans detect the annotations. Listeners nest job -> step -> chunk -> item and fire in registration order.

open as a page