JobExecutionListener in Spring Batch (Complete Guide)
When building enterprise batch applications, you cannot simply deploy a job and hope it succeeds silently in the background. You need strict observability. You must know exactly when a job starts, how long it takes to run, and most importantly, you need immediate alerts if the job fails.
This is where the JobExecutionListener comes in. It is one of the most powerful callback interfaces in the Spring Batch framework, allowing you to inject custom business logic before a job begins and after a job completes.
๐บ Visual Learner? Explore the Spring Batch Masterclass!
Master the entire Spring Batch framework—including Listeners, Chunk Processing, Tasklets, and Fault Tolerance—by watching my complete video series.
1. What Is JobExecutionListener?
The JobExecutionListener interface provides two simple interception methods that wrap your entire batch job lifecycle:
public interface JobExecutionListener {
void beforeJob(JobExecution jobExecution);
void afterJob(JobExecution jobExecution);
}
Through the JobExecution object passed into these methods, you gain full access to the job's metadata, including its running status, failure exceptions, start times, and the ExecutionContext (a key-value store used to share data across the batch job).
2. Real-World Enterprise Use Cases
In production environments, developers rarely use listeners just to print logs. Common enterprise use cases include:
- Alerting & Notifications: Sending an email or Slack message via
afterJob()if the job status evaluates toBatchStatus.FAILED. - Performance Auditing: Calculating the exact millisecond duration of a job and persisting it to a monitoring dashboard like Grafana.
- Resource Management: Opening database connections or cleaning up temporary SFTP download folders in
beforeJob().
3. Creating a Production-Ready Listener
Let's build a realistic JobCompletionNotificationListener. In this example, we use the beforeJob method to record the exact start time in the ExecutionContext. In the afterJob method, we calculate the execution duration.
Most importantly, we check the BatchStatus. If the job fails, we extract the exceptions so we can trigger an alert.
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class JobCompletionNotificationListener implements JobExecutionListener {
@Override
public void beforeJob(JobExecution jobExecution) {
log.info("๐ Job [{}] is starting...", jobExecution.getJobInstance().getJobName());
// Store start time in the ExecutionContext to share it with afterJob()
jobExecution.getExecutionContext().putLong("startTimeMillis", System.currentTimeMillis());
}
@Override
public void afterJob(JobExecution jobExecution) {
// Retrieve the start time from the context
long startTime = jobExecution.getExecutionContext().getLong("startTimeMillis", System.currentTimeMillis());
long duration = System.currentTimeMillis() - startTime;
if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
log.info("✅ Job Completed Successfully in {} ms", duration);
} else if (jobExecution.getStatus() == BatchStatus.FAILED) {
log.error("❌ Job FAILED after {} ms. Exceptions: {}", duration, jobExecution.getAllFailureExceptions());
// TODO: Integrate email service or PagerDuty API here to alert the engineering team
}
}
}
4. Registering the Listener in Spring Batch 5
To activate the listener, you must register it inside your JobBuilder. Notice that the listener is attached to the Job level, not the Step level.
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class SpringBatchConfig {
@Bean
public Job sampleJob(JobRepository jobRepository,
Step sampleStep,
JobCompletionNotificationListener listener) {
return new JobBuilder("sample-job", jobRepository)
.listener(listener) // Registering our custom listener here
.start(sampleStep)
.build();
}
@Bean
public Step sampleStep(JobRepository jobRepository, PlatformTransactionManager txManager) {
return new StepBuilder("sample-step", jobRepository)
.tasklet((contribution, chunkContext) -> {
System.out.println("Executing core step business logic...");
return RepeatStatus.FINISHED;
}, txManager)
.build();
}
}
5. Frequently Asked Questions
Can I register multiple JobExecutionListeners?
Yes. You can chain multiple listeners together by calling .listener() multiple times in the JobBuilder. They will execute in the order they were registered for beforeJob, and in reverse order for afterJob.
How is JobExecutionListener different from StepExecutionListener?
A JobExecutionListener runs exactly once per job. A StepExecutionListener runs once for every step inside that job. If your job has 5 steps, the Step listener triggers 5 times.
Can a listener prevent a job from running?
Yes. If you throw a RuntimeException inside the beforeJob() method, Spring Batch will immediately halt execution, mark the job as FAILED, and the subsequent steps will never run.
Conclusion
The JobExecutionListener is an indispensable tool for building resilient batch architectures. By moving your logging, metrics tracking, and error-alerting logic out of your core processing steps and into a listener, you adhere to the Single Responsibility Principle and make your codebase significantly easier to maintain.
๐ Related Spring Batch Monitoring & Execution Guides
Enhance your understanding of job execution monitoring by exploring related Spring Batch concepts such as error handling, retry strategies, and conditional flows.
๐งฑ Spring Batch Core Components
Understand how JobExecutionListener integrates with JobRepository and the Step lifecycle.
๐ซ Skip Policy & Error Handling
Track skipped items, failures, and step outcomes using advanced listener callbacks.
๐ Retry Mechanism
Monitor retry attempts and retry exhaustion events during heavy batch executions.
๐ Conditional Flow in Jobs
Drive conditional job paths using the execution status captured by your listeners.
๐งต Multithreaded Step
Observe execution behavior and performance metrics in parallel batch processing scenarios.
⚙️ Spring Batch Tasklet
Combine Job Listeners with Tasklet execution status to build powerful, single-pass utilities.